You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1695 lines
49 KiB

#include "private.h"
#include <Ecore.h>
#include <math.h>
#define HACK_FLUSH_EVENTS_DURING_LONG_CALCULATE 1
#ifdef HACK_FLUSH_EVENTS_DURING_LONG_CALCULATE
static const double LONG_CALCULATE_TIMEOUT = 0.6;
#include <Ecore_X.h>
#include <X11/Xlib.h>
#endif
/* value to consider as double/float error, differences smaller than
* this are ignored
*/
static const double DOUBLE_ERROR = 0.00001;
/* constant to multiply offsets.
*
* The smaller the value, the faster it will stop.
*/
static const double KINETIC_FRICTION = 1.0;
/* Minimum value to consider velocity worth doing kinetic animation */
static const Evas_Coord KINETIC_VELOCITY_THRESHOLD = 20;
/* How long the auto zoom animation should take, in seconds */
static const double ZOOM_AUTO_ANIMATION_DURATION = 0.5;
/* Timeout in seconds to consider double-click an auto zoom */
static const double ZOOM_AUTO_TIMEOUT = 0.2;
/* Padding in pixels around object being zoomed */
static const Evas_Coord ZOOM_AUTO_PADDING = 15;
static const float ZOOM_AUTO_MIN = 0.3;
static const float ZOOM_AUTO_MAX = 4.0;
static const float ZOOM_AUTO_MIN_DIFFERENCE = 0.01;
/* How long the interactive zoom animation should take, in seconds */
static const double ZOOM_STEP_ANIMATION_DURATION = 0.8;
/* Threshold in pixels to change zoom step. */
static const Evas_Coord ZOOM_STEP_THRESHOLD = 50;
static const float ZOOM_STEPS[] = {
0.3, 0.5, 0.67, 0.8, 0.9, 1.0, 1.1, 1.2, 1.33, 1.5, 1.7, 2.0, 2.4, 3.0,
};
static const unsigned int ZOOM_STEPS_LAST = sizeof(ZOOM_STEPS) / sizeof(ZOOM_STEPS[0]);
/*
* Structure filled by ewk_view_single_smart_set() from
* view_add(). This contains the pointers to vanilla/pristine
* ewk_view_single, which will be used to implement super() behavior
* in C.
*/
static Ewk_View_Smart_Class _parent_sc = EWK_VIEW_SMART_CLASS_INIT_NULL;
typedef struct _View_Smart_Data View_Smart_Data;
#define VIEW_SD_GET_OR_RETURN(o, sd, ...)\
View_Smart_Data * sd = evas_object_smart_data_get(o);\
if (!sd)\
{\
CRITICAL("no smart data for object %p [%s]",\
o, evas_object_type_get(o));\
return __VA_ARGS__;\
}
#define EWK_VIEW_SD_GET_OR_RETURN(o, sd, ...)\
Ewk_View_Smart_Data * sd = evas_object_smart_data_get(o);\
if (!sd)\
{\
CRITICAL("no smart data for object %p [%s]",\
o, evas_object_type_get(o));\
return __VA_ARGS__;\
}
struct point_history
{
Evas_Coord x, y;
double timestamp;
};
/* Extends Ewk_View_Smart_Data to add some members we'll need */
struct _View_Smart_Data
{
/* Original type must be the first, so ewk_view and ewk_view_single know
* how to access it (the memory offsets will remain the same).
*/
Ewk_View_Smart_Data base;
/* where to keep track of our animators */
struct
{
Ecore_Animator *pan;
Ecore_Animator *zoom;
Ecore_Animator *kinetic;
} animator;
Ecore_Idler *idler_close_window;
/* context used during pan/scroll animator */
struct
{
/* circular array with collected samples (see _view_smart_mouse_move()) */
#define PAN_HISTORY_SIZE (20)
struct point_history history[PAN_HISTORY_SIZE];
unsigned int idx; /* index in the circular buffer of the last stored value */
unsigned int count; /* total number of samples collected */
struct point_history last_move; /* used by _view_animator_pan() */
} pan;
Evas_Event_Mouse_Down mouse_down_copy; /* copy of down event, used by _view_smart_mouse_up() */
struct
{
struct
{
float zoom;
Evas_Coord x, y;
double timestamp;
int step_idx;
} start;
struct
{
Evas_Coord y;
float zoom;
double timestamp;
int step_idx;
} last, next;
struct
{
float zoom;
double time;
} diff;
float min_zoom;
Eina_Bool interactive : 1;
} zoom;
/* context used during kinetic animator */
struct
{
/* starting point */
struct
{
Evas_Coord dx, dy;
double timestamp;
} start;
/* last run */
struct
{
Evas_Coord dx, dy;
} last;
/* flags to state we ended animation in those axis */
struct
{
Eina_Bool x : 1;
Eina_Bool y : 1;
} done;
} kinetic;
/* general flags */
struct
{
Eina_Bool first_calculate : 1;
Eina_Bool animated_zoom : 1;
Eina_Bool touch_interface : 1;
} flags;
Evas_Object *context_menu;
};
/***********************************************************************
* Helper functions *
**********************************************************************/
/* calculates one good zoom level to fit given hit test result */
static float
_view_zoom_fits_hit(View_Smart_Data *sd, const Ewk_Hit_Test *hit_test)
{
Evas_Coord x, y; /* hit test point */
Evas_Coord bx, by, bw, bh, bw1, bw2, bh1, bh2; /* bounding box */
Evas_Coord vx, vy, vw, vh, vw1, vw2, vh1, vh2; /* visible content */
Evas_Coord cw, ch;
float zx, zy, z, old_zoom, zx1, zx2, zy1, zy2;
if (!hit_test)
return 0.0;
old_zoom = ewk_frame_zoom_get(sd->base.main_frame);
/* save some typing */
x = hit_test->x;
y = hit_test->y;
bx = hit_test->bounding_box.x;
by = hit_test->bounding_box.y;
bw = hit_test->bounding_box.w;
bh = hit_test->bounding_box.h;
if ((bw <= 0) || (bh <= 0))
return 0.0;
ewk_frame_visible_content_geometry_get
(sd->base.main_frame, EINA_FALSE, &vx, &vy, &vw, &vh);
if ((vw <= 0) || (vh <= 0))
return 0.0;
ewk_frame_contents_size_get(sd->base.main_frame, &cw, &ch);
if ((cw <= 0) || (ch <= 0))
return 0.0;
/* split width of left and right parts from hit test position */
bw1 = x - bx;
bw2 = bw - bw1;
/* split height of top and bottom parts from hit test position */
bh1 = y - by;
bh2 = bh - bh1;
/* split size of viewport as well. We'll use this as the available space */
vw1 = x - vx;
vw2 = vw - vw1;
vh1 = y - vy;
vh2 = vh - vh1;
/* check if we can add padding around those sizes, "adding" if possible
* (we actually subtract as adding a pad is removing available space)
*/
if (vw1 > ZOOM_AUTO_PADDING)
vw1 -= ZOOM_AUTO_PADDING;
if (vw2 > ZOOM_AUTO_PADDING)
vw2 -= ZOOM_AUTO_PADDING;
if (vh1 > ZOOM_AUTO_PADDING)
vh1 -= ZOOM_AUTO_PADDING;
if (vh2 > ZOOM_AUTO_PADDING)
vh2 -= ZOOM_AUTO_PADDING;
/* check individual zoom to fit each part */
zx1 = (bw1 > 0) ? (vw1 / (float)bw1) : 0.0;
zx2 = (bw2 > 0) ? (vw2 / (float)bw2) : 0.0;
zy1 = (bh1 > 0) ? (vh1 / (float)bh1) : 0.0;
zy2 = (bh2 > 0) ? (vh2 / (float)bh2) : 0.0;
if ((zx1 >= ZOOM_AUTO_MIN_DIFFERENCE) && (zx2 >= ZOOM_AUTO_MIN_DIFFERENCE))
zx = (zx1 < zx2) ? zx1 : zx2;
else if (zx1 >= ZOOM_AUTO_MIN_DIFFERENCE)
zx = zx1;
else
zx = zx2;
if ((zy1 >= ZOOM_AUTO_MIN_DIFFERENCE) && (zy2 >= ZOOM_AUTO_MIN_DIFFERENCE))
zy = (zy1 < zy2) ? zy1 : zy2;
else if (zy1 >= ZOOM_AUTO_MIN_DIFFERENCE)
zy = zy1;
else
zy = zy2;
if ((zx >= ZOOM_AUTO_MIN_DIFFERENCE) && (zy >= ZOOM_AUTO_MIN_DIFFERENCE))
z = (zx < zy) ? zx : zy;
else if (zx >= ZOOM_AUTO_MIN_DIFFERENCE)
z = zx;
else
z = zy;
/* zoom will make contents be smaller than viewport, limit it */
if (((int)(z * old_zoom * cw) < vw) || ((int)(z * old_zoom * ch) < vh))
{
float ac = cw / (float)ch;
float av = vw / (float)vh;
if (ac < av)
z = vw / (float)cw;
else
z = vh / (float)ch;
}
#if 0
/* debug */
printf(">>> fit: center=%3d,%3d box=%3d,%3d+%3dx%3d\n"
" x: %3d = %3d + %3d, %3d = %3d + %3d, %2.4f %2.4f -> %2.4f\n"
" y: %3d = %3d + %3d, %3d = %3d + %3d, %2.4f %2.4f -> %2.4f\n"
" final: %2.4f %2.4f (old=%0.3f, difference=%0.3f)\n"
" contents: %4dx%4d -> %4dx%4d\n"
"\n",
x, y, bx, by, bw, bh,
bw, bw1, bw2, vw, vw1, vw2, zx1, zx2, zx,
bh, bh1, bh2, vh, vh1, vh2, zy1, zy2, zy,
z, old_zoom * z, old_zoom, fabs(old_zoom - z),
cw, ch, (int)(z * old_zoom * cw), (int)(z * old_zoom * ch));
#endif
z *= old_zoom;
if (z < ZOOM_AUTO_MIN)
z = ZOOM_AUTO_MIN;
else if (z > ZOOM_AUTO_MAX)
z = ZOOM_AUTO_MAX;
if (fabs(old_zoom - z) < ZOOM_AUTO_MIN_DIFFERENCE)
return 0.0;
return z;
}
/* remove flag saying that object is animating zoom */
static void
_view_zoom_animated_end(void *data, Evas_Object *view __UNUSED__, void *event_info __UNUSED__)
{
View_Smart_Data *sd = data;
sd->flags.animated_zoom = EINA_FALSE;
}
/* ask for pre-render when load finished */
static void
_view_load_finished(void *data, Evas_Object *view, void *event_info __UNUSED__)
{
View_Smart_Data *sd = data;
float zoom = ewk_frame_zoom_get(sd->base.main_frame);
Evas_Coord x, y, w, h;
ewk_frame_visible_content_geometry_get
(sd->base.main_frame, EINA_TRUE, &x, &y, &w, &h);
w *= 2;
h *= 2;
INF("load finished, pre-render %d,%d+%dx%d at %0.2f", x, y, w, h, zoom);
ewk_view_pre_render_region(view, x, y, w, h, zoom);
}
/* stop animators, we changed page */
static void
_view_uri_changed(void *data, Evas_Object *view, void *event_info __UNUSED__)
{
View_Smart_Data *sd = data;
if (sd->animator.pan)
{
ecore_animator_del(sd->animator.pan);
sd->animator.pan = NULL;
}
if (sd->animator.kinetic)
{
ecore_animator_del(sd->animator.kinetic);
sd->animator.kinetic = NULL;
}
if (sd->animator.zoom)
{
/* inform ewk_view that we finished performing zoom animation */
ewk_view_zoom_animated_mark_stop(view);
evas_object_smart_callback_call(view, "zoom,interactive,end", NULL);
ecore_animator_del(sd->animator.zoom);
sd->animator.zoom = NULL;
}
}
/* ask ewk_view to pre render in the given direction.
*
* This uses a heuristics to create a pre-render region using the
* given motion vector.
*/
static void
_view_pan_pre_render(View_Smart_Data *sd, Evas_Coord dx, Evas_Coord dy)
{
float zoom = ewk_frame_zoom_get(sd->base.main_frame);
double weightx, weighty;
Evas_Coord x, y, w, h, px, py, pw, ph;
unsigned int vx, vy;
/* where are we now */
ewk_frame_visible_content_geometry_get
(sd->base.main_frame, EINA_TRUE, &x, &y, &w, &h);
/* get absolute value for dx and dy, remove it's precision */
vx = abs(dx) >> 4;
vy = abs(dy) >> 4;
if (vx == 0 && vy == 0)
return; /* motion vector is not significant, don't pre-render */
else if (vx < vy)
{
/* moving more on Y-axis than X. */
weightx = 0.5;
weighty = 1.0;
}
else if (vx > vy)
{
/* moving more on X-axis than X. */
weightx = 1.0;
weighty = 0.5;
}
else
{
/* moving equally on both axis, be more conservative */
weightx = 0.6;
weighty = 0.6;
}
/* if values were not significant, zero their extra weight */
if (vx == 0)
weightx = 0.0;
else if (vy == 0)
weighty = 0.0;
/* use single region that includes existing viewport.
*
* This is simple and should work. Another option is to do more
* detailed analysis and possible create special cases for
* vertical, horizontal and diagonal moves, with diagonal being
* smarter and rendering less than we do now.
*/
pw = w * (1.0 + weightx);
ph = h * (1.0 + weighty);
px = x;
py = y;
if (dx > 0)
px -= (pw - w);
if (dy > 0)
py -= (ph - h);
INF("pre-render region %d,%d+%dx%d at %0.2f (viewport=%d,%d+%dx%d)",
px, py, pw, ph, zoom, x, y, w, h);
ewk_view_pre_render_region(sd->base.self, px, py, pw, ph, zoom);
}
static unsigned int
_view_zoom_closest_index_find(float zoom)
{
unsigned int i, close_idx;
float close_zoom;
close_idx = 0;
close_zoom = 999999.99;
for (i = 0; i < ZOOM_STEPS_LAST; i++)
{
float cur = fabs(zoom - ZOOM_STEPS[i]);
if (cur < close_zoom)
{
close_idx = i;
close_zoom = cur;
if (cur < DOUBLE_ERROR) /* close enough */
break;
}
}
return close_idx;
}
/***********************************************************************
* Animators: shared timers at fixed frame rate/interval. *
**********************************************************************/
/* Animator that implements the kinetic animation (momentum).
*
* This is started by _view_smart_mouse_up() if it is worth doing the
* animation.
*
* Code is heavily based on els_scroller.c from Elementary.
*/
static Eina_Bool
_view_animator_kinetic(void *data)
{
View_Smart_Data *sd = data;
double p, dt, now = ecore_loop_time_get();
Evas_Coord x, y, sx, sy, sw, sh;
dt = now - sd->kinetic.start.timestamp;
/* time difference is too small, ignore it */
if (dt <= DOUBLE_ERROR)
goto end;
dt = dt / KINETIC_FRICTION;
if (dt > 1.0)
dt = 1.0;
p = 1.0 - ((1.0 - dt) * (1.0 - dt));
/* query scroll area and current position */
ewk_frame_scroll_pos_get(sd->base.main_frame, &sx, &sy);
ewk_frame_scroll_size_get(sd->base.main_frame, &sw, &sh);
if (!sd->kinetic.done.x)
{
/* we're not done on x-axis yet, calculate new displacement */
Evas_Coord dx = sd->kinetic.start.dx * KINETIC_FRICTION * p;
x = sd->kinetic.last.dx - dx;
sd->kinetic.last.dx = dx;
/* check if new displacement fit in scroll area */
if (sx + x < 0)
{
x = -sx;
sd->kinetic.done.x = EINA_TRUE;
}
else if (sx + x >= sw)
{
x = sw - sx;
sd->kinetic.done.x = EINA_TRUE;
}
}
else
x = 0;
if (!sd->kinetic.done.y)
{
/* we're not done on y-axis yet, calculate new displacement */
Evas_Coord dy = sd->kinetic.start.dy * KINETIC_FRICTION * p;
y = sd->kinetic.last.dy - dy;
sd->kinetic.last.dy = dy;
/* check if new displacement fit in scroll area */
if (sy + y < 0)
{
y = -sy;
sd->kinetic.done.y = EINA_TRUE;
}
else if (sy + y >= sh)
{
y = sh - sy;
sd->kinetic.done.y = EINA_TRUE;
}
}
else
y = 0;
/* if there is anything to scroll, ask it */
if (x != 0 || y != 0)
ewk_frame_scroll_add(sd->base.main_frame, x, y);
/* if we finished our work, just stop the animator */
if (dt >= 1.0 || (sd->kinetic.done.x && sd->kinetic.done.y))
{
_view_pan_pre_render(sd, sd->kinetic.start.dx, sd->kinetic.start.dy);
sd->animator.kinetic = NULL;
return ECORE_CALLBACK_CANCEL;
}
end:
return ECORE_CALLBACK_RENEW; /* keep running until we finish */
}
/* Animator to apply scroll/pan at fixed frame rate.
*
* If pointer (mouse) was moved since last run, then request main
* frame to scroll.
*/
static Eina_Bool
_view_animator_pan(void *data)
{
View_Smart_Data *sd = data;
Evas_Coord x, y, dx, dy;
double timestamp;
evas_pointer_canvas_xy_get(sd->base.base.evas, &x, &y);
/* webkit is swapped */
dx = sd->pan.last_move.x - x;
dy = sd->pan.last_move.y - y;
/* ignore this sample if it did not change */
if ((dx == 0) && (dy == 0))
goto end;
timestamp = ecore_loop_time_get();
if (timestamp <= sd->pan.last_move.timestamp) /* did time went backwards?! */
goto end;
/* request scroll by same displacement */
ewk_frame_scroll_add(sd->base.main_frame, dx, dy);
/* save new last position */
sd->pan.last_move.x = x;
sd->pan.last_move.y = y;
sd->pan.last_move.timestamp = timestamp;
end:
return ECORE_CALLBACK_RENEW; /* keep running until something else remove */
}
/* start pan/scroll animation */
static void
_view_pan_start(View_Smart_Data *sd, const Evas_Event_Mouse_Down *ev)
{
struct point_history *p;
/* not really required, but let's clean it */
memset(&sd->pan, 0, sizeof(sd->pan));
/* start history with one sample being the down position */
sd->pan.idx = 0;
sd->pan.count = 1;
p = sd->pan.history;
p->x = ev->canvas.x;
p->y = ev->canvas.y;
p->timestamp = ecore_loop_time_get();
sd->pan.last_move = *p;
ewk_view_pre_render_cancel(sd->base.self);
/* register function to collect samples and apply scrolls at fixed interval*/
if (!sd->animator.pan)
sd->animator.pan = ecore_animator_add(_view_animator_pan, sd);
}
/* stop pan animation and possible schedule a kinetic scrolling.
*
* @return @c EINA_TRUE if event was handled inside this function and
* should not be used by calling function or @c EINA_FALSE if
* event is unused and should be forwarded by caller.
*/
static Eina_Bool
_view_pan_stop(View_Smart_Data *sd, const Evas_Event_Mouse_Up *ev)
{
double t, at;
Evas_Coord ax, ay, dx, dy, vel;
unsigned int i, todo, samples;
/* Stop pan animator, we're done already. */
if (sd->animator.pan)
{
ecore_animator_del(sd->animator.pan);
sd->animator.pan = NULL;
}
/* If we do not have enough samples, just assume it was a click */
if (sd->pan.count < 2)
return EINA_FALSE;
/* the following code is heavily based on els_scroller.c from Elementary.
*
* It will check the samples from history and get the average point.
*/
t = ecore_loop_time_get();
ax = ev->canvas.x;
ay = ev->canvas.y;
at = 0.0;
i = sd->pan.idx;
todo = sd->pan.count > PAN_HISTORY_SIZE ? PAN_HISTORY_SIZE : sd->pan.count;
samples = 0;
for (; todo > 0; todo--)
{
struct point_history *p = sd->pan.history + i;
double dt = t - p->timestamp;
if (dt > 0.2 && samples > 0)
break;
at += dt;
ax += p->x;
ay += p->y;
samples++;
if (i > 0)
i--;
else
i = PAN_HISTORY_SIZE - 1;
}
/* time was too short, consider it a click */
if (at <= DOUBLE_ERROR)
return EINA_FALSE;
ax /= samples + 1;
ay /= samples + 1;
at *= 4.0;
dx = ev->canvas.x - ax;
dy = ev->canvas.y - ay;
vel = sqrt((dx * dx) + (dy * dy)) / at;
/* velocity was too short, consider it a click */
if (vel <= KINETIC_VELOCITY_THRESHOLD)
{
_view_pan_pre_render(sd, dx, dy);
return EINA_FALSE;
}
/* it's really woth animating. setup kinetic animation context and start
* animator function.
*/
sd->kinetic.start.timestamp = t;
sd->kinetic.start.dx = dx;
sd->kinetic.start.dy = dy;
sd->kinetic.last.dx = 0;
sd->kinetic.last.dy = 0;
sd->kinetic.done.x = EINA_FALSE;
sd->kinetic.done.y = EINA_FALSE;
sd->animator.kinetic = ecore_animator_add(_view_animator_kinetic, sd);
return EINA_TRUE;
}
/* Animator to collect mouse position and apply zoom at fixed frame rate.
*
* This animator works in two parts:
*
* 1. check if mouse moved up/down enough to change the zoom level
* to another band. We move in bands specified in ZOOM_STEPS every
* ZOOM_STEP_THRESHOLD pixels, up or down.
*
* 2. interpolates zoom level using weak zoom progressively until
* we reach desired level (sd->zoom.next.zoom) in calculated
* time slot (based on ZOOM_STEP_ANIMATION_DURATION).
*/
static Eina_Bool
_view_animator_zoom(void *data)
{
View_Smart_Data *sd = data;
Evas_Object *o = sd->base.self;
Evas_Coord y, dy;
double timestamp = ecore_loop_time_get();
int step_inc, idx;
/* wait to know if we're in auto zoom or interactive zooming */
if (timestamp - sd->zoom.start.timestamp <= ZOOM_AUTO_TIMEOUT)
goto end;
if (!sd->zoom.interactive)
{
evas_object_smart_callback_call(o, "zoom,interactive,start", NULL);
sd->zoom.interactive = EINA_TRUE;
}
evas_pointer_canvas_xy_get(sd->base.base.evas, NULL, &y);
dy = y - sd->zoom.start.y;
step_inc = dy / ZOOM_STEP_THRESHOLD;
/* find out new step */
idx = step_inc + sd->zoom.start.step_idx;
/* check for out of bounds access */
if (idx < 0)
idx = 0;
else if (idx >= (int)ZOOM_STEPS_LAST)
idx = ZOOM_STEPS_LAST - 1;
if (ZOOM_STEPS[idx] < sd->zoom.min_zoom)
idx = sd->zoom.next.step_idx;
/* Part 1. check if mouse moved enough to change zoom level band */
if (idx != sd->zoom.next.step_idx)
{
View_Zoom_Interactive data;
/* new target (next) values */
sd->zoom.next.step_idx = idx;
sd->zoom.next.zoom = ZOOM_STEPS[idx];
sd->zoom.next.timestamp = timestamp + ZOOM_STEP_ANIMATION_DURATION;
sd->zoom.diff.zoom = sd->zoom.next.zoom - sd->zoom.last.zoom;
sd->zoom.diff.time = sd->zoom.next.timestamp - sd->zoom.last.timestamp;
/* we'll animated, disable smooth scaling in evas so it's faster */
ewk_view_zoom_weak_smooth_scale_set(o, EINA_FALSE);
/* inform user that new level was requested */
data.x = sd->zoom.start.x;
data.y = sd->zoom.start.y;
data.zoom = sd->zoom.next.zoom;
evas_object_smart_callback_call(o, "zoom,interactive", &data);
}
if (sd->zoom.next.timestamp < DOUBLE_ERROR)
goto end; /* sd->zoom.next.timestamp is zero, we're stopped */
/* Part 2. interpolate values to animate zoom change */
else if (timestamp >= sd->zoom.next.timestamp)
{
/* we're done, enable smooth scaling so the still image looks better
* and apply the final zoom level
*/
ewk_view_zoom_weak_smooth_scale_set(o, EINA_TRUE);
ewk_view_zoom_weak_set
(o, sd->zoom.next.zoom, sd->zoom.start.x, sd->zoom.start.y);
sd->zoom.last = sd->zoom.next;
sd->zoom.next.timestamp = 0.0; /* say we're stopped, see above */
}
else if (sd->zoom.diff.time > 0.0)
{
/* regular intermediate animation frame, interpolate and apply */
float zoom, p;
p = (timestamp - sd->zoom.last.timestamp) / sd->zoom.diff.time;
zoom = sd->zoom.last.zoom + sd->zoom.diff.zoom * p;
ewk_view_zoom_weak_set(o, zoom, sd->zoom.start.x, sd->zoom.start.y);
}
end:
return ECORE_CALLBACK_RENEW; /* keep running until something else remove */
}
/* start zoom animation */
static void
_view_zoom_start(View_Smart_Data *sd, const Evas_Event_Mouse_Down *ev)
{
Evas_Object *frame = sd->base.main_frame;
Evas_Coord cw, ch, vw, vh;
float z, zx, zy;
ewk_view_pre_render_cancel(sd->base.self);
/* remember starting point so we have a reference */
sd->zoom.start.zoom = ewk_frame_zoom_get(frame);
sd->zoom.start.x = ev->canvas.x;
sd->zoom.start.y = ev->canvas.y;
sd->zoom.start.timestamp = ecore_loop_time_get();
/* find out which zoom level band we're in, closest match */
sd->zoom.start.step_idx = _view_zoom_closest_index_find(sd->zoom.start.zoom);
sd->zoom.last.y = sd->zoom.start.x;
sd->zoom.last.zoom = sd->zoom.start.zoom;
sd->zoom.last.timestamp = sd->zoom.start.timestamp;
sd->zoom.next.y = sd->zoom.start.x;
sd->zoom.next.zoom = sd->zoom.start.zoom;
sd->zoom.next.timestamp = 0.0; /* this means we're stopped */
sd->zoom.diff.zoom = 0.0;
sd->zoom.diff.time = 0.0;
sd->zoom.interactive = EINA_FALSE;
/* find out the minimum zoom level that contents is greater or equal
* viewport size
*/
ewk_frame_visible_content_geometry_get(frame, 0, NULL, NULL, &vw, &vh);
ewk_frame_contents_size_get(frame, &cw, &ch);
zx = vw / (float)cw;
zy = vh / (float)ch;
z = (zx > zy) ? zx : zy;
z *= sd->zoom.start.zoom;
if (z >= ZOOM_STEPS[0])
sd->zoom.min_zoom = z;
else
sd->zoom.min_zoom = ZOOM_STEPS[0];