svn update: 49550 (latest:49550)
[framework/uifw/elementary.git] / src / lib / elm_flip.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Flip Flip
6  *
7  * This holds 2 content objects: one on the front and one on the back. It
8  * allows you to flip from front to back and vice-versa using various effects.
9  *
10  * Supported flip types:
11  * ELM_FLIP_ROTATE_Y_CENTER_AXIS
12  * ELM_FLIP_ROTATE_X_CENTER_AXIS
13  * ELM_FLIP_ROTATE_XZ_CENTER_AXIS
14  * ELM_FLIP_ROTATE_YZ_CENTER_AXIS
15  *
16  * Signals that you can add callbacks for are:
17  *
18  * "animate,done" - when a flip animation is finished
19  */
20
21 typedef struct _Widget_Data Widget_Data;
22
23 struct _Widget_Data
24 {
25    Ecore_Animator *animator;
26    double start, len;
27    Elm_Flip_Mode mode;
28    struct {
29       Evas_Object *content, *clip;
30    } front, back;
31    Eina_Bool state : 1;
32 };
33
34 static const char *widtype = NULL;
35 static void _del_hook(Evas_Object *obj);
36 static void _theme_hook(Evas_Object *obj);
37 static void _sizing_eval(Evas_Object *obj);
38 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
39 static void _sub_del(void *data, Evas_Object *obj, void *event_info);
40
41 static void _configure(Evas_Object *obj);
42
43 static void
44 _del_hook(Evas_Object *obj)
45 {
46    Widget_Data *wd = elm_widget_data_get(obj);
47    if (!wd) return;
48    if (wd->animator) ecore_animator_del(wd->animator);
49    free(wd);
50 }
51
52 static void
53 _theme_hook(Evas_Object *obj)
54 {
55    Widget_Data *wd = elm_widget_data_get(obj);
56    if (!wd) return;
57    _sizing_eval(obj);
58 }
59
60 static void
61 _sizing_eval(Evas_Object *obj)
62 {
63    Widget_Data *wd = elm_widget_data_get(obj);
64    Evas_Coord minw = -1, minh = -1, minw2 = -1, minh2 = -1;
65    Evas_Coord maxw = -1, maxh = -1, maxw2 = -1, maxh2 = -1;
66    if (!wd) return;
67    if (wd->front.content)
68      evas_object_size_hint_min_get(wd->front.content, &minw, &minh);
69    if (wd->back.content)
70      evas_object_size_hint_min_get(wd->back.content, &minw2, &minh2);
71    if (wd->front.content)
72      evas_object_size_hint_max_get(wd->front.content, &maxw, &maxh);
73    if (wd->back.content)
74      evas_object_size_hint_max_get(wd->back.content, &maxw2, &maxh2);
75    
76    if (minw2 > minw) minw = minw2;
77    if (minh2 > minh) minh = minh2;
78    if ((maxw2 >= 0) && (maxw2 < maxw)) maxw = maxw2;
79    if ((maxh2 >= 0) && (maxh2 < maxh)) maxh = maxh2;
80    
81    evas_object_size_hint_min_set(obj, minw, minh);
82    evas_object_size_hint_max_set(obj, maxw, maxh);
83 }
84
85 static void
86 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
87 {
88    Widget_Data *wd = elm_widget_data_get(data);
89    if (!wd) return;
90    _sizing_eval(data);
91 }
92
93 static void
94 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info)
95 {
96    Widget_Data *wd = elm_widget_data_get(obj);
97    Evas_Object *sub = event_info;
98    if (!wd) return;
99    if (sub == wd->front.content)
100      {
101         evas_object_event_callback_del_full(sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
102                                             _changed_size_hints, obj);
103         wd->front.content = NULL;
104         evas_object_hide(wd->front.clip);
105         _sizing_eval(obj);
106      }
107    else if (sub == wd->back.content)
108      {
109         evas_object_event_callback_del_full(sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
110                                             _changed_size_hints, obj);
111         wd->back.content = NULL;
112         evas_object_hide(wd->back.clip);
113         _sizing_eval(obj);
114      }
115 }
116
117 static int
118 _flip(Evas_Object *obj)
119 {
120    Widget_Data *wd = elm_widget_data_get(obj);
121    double t = ecore_loop_time_get() - wd->start;
122    Evas_Coord x, y, w, h;
123    double p, deg;
124    Evas_Map *mf, *mb;
125    Evas_Coord cx, cy, px, py, foc;
126    int lx, ly, lz, lr, lg, lb, lar, lag, lab;
127    if (!wd->animator) return 0;
128    t = t / wd->len;
129    if (t > 1.0) t = 1.0;
130
131    if (!wd) return 0;
132    evas_object_geometry_get(obj, &x, &y, &w, &h);
133
134    mf = evas_map_new(4);
135    evas_map_smooth_set(mf, 0);
136    mb = evas_map_new(4);
137    evas_map_smooth_set(mb, 0);
138
139    if (wd->front.content)
140      evas_map_util_points_populate_from_object_full(mf, wd->front.content, 0);
141    if (wd->back.content)
142      evas_map_util_points_populate_from_object_full(mb, wd->back.content, 0);
143    
144    cx = x + (w / 2);
145    cy = y + (h / 2);
146
147    px = x + (w / 2);
148    py = y + (h / 2);
149    foc = 2048;
150    
151    lx = cx;
152    ly = cy;
153    lz = -10000;
154    lr = 255;
155    lg = 255;
156    lb = 255;
157    lar = 0;
158    lag = 0;
159    lab = 0;
160    
161    switch (wd->mode)
162      {
163      case ELM_FLIP_ROTATE_Y_CENTER_AXIS:
164         p = 1.0 - t;
165         p = 1.0 - (p * p);
166         if (wd->state) deg = 180.0 * p;
167         else deg = 180 + (180.0 * p);
168         evas_map_util_3d_rotate(mf, 0.0, deg, 0.0, cx, cy, 0);
169         evas_map_util_3d_rotate(mb, 0.0, deg + 180.0, 0.0, cx, cy, 0);
170         break;
171      case ELM_FLIP_ROTATE_X_CENTER_AXIS:
172         p = 1.0 - t;
173         p = 1.0 - (p * p);
174         if (wd->state) deg = 180.0 * p;
175         else deg = 180 + (180.0 * p);
176         evas_map_util_3d_rotate(mf, deg, 0.0, 0.0, cx, cy, 0);
177         evas_map_util_3d_rotate(mb, deg + 180.0, 0.0, 0.0, cx, cy, 0);
178         break;
179      case ELM_FLIP_ROTATE_XZ_CENTER_AXIS:
180         p = 1.0 - t;
181         p = 1.0 - (p * p);
182         if (wd->state) deg = 180.0 * p;
183         else deg = 180 + (180.0 * p);
184         evas_map_util_3d_rotate(mf, deg, 0.0, deg, cx, cy, 0);
185         evas_map_util_3d_rotate(mb, deg + 180.0, 0.0, deg + 180.0, cx, cy, 0);
186         break;
187      case ELM_FLIP_ROTATE_YZ_CENTER_AXIS:
188         p = 1.0 - t;
189         p = 1.0 - (p * p);
190         if (wd->state) deg = 180.0 * p;
191         else deg = 180 + (180.0 * p);
192         evas_map_util_3d_rotate(mf, 0.0, deg, deg, cx, cy, 0);
193         evas_map_util_3d_rotate(mb, 0.0, deg + 180.0, deg + 180.0, cx, cy, 0);
194         break;
195      default:
196         break;
197      }
198
199    
200    if (wd->front.content)
201      {
202         evas_map_util_3d_lighting(mf, lx, ly, lz, lr, lg, lb, lar, lag, lab);
203         evas_map_util_3d_perspective(mf, px, py, 0, foc);
204         evas_object_map_set(wd->front.content, mf);
205         evas_object_map_enable_set(wd->front.content, 1);
206         if (evas_map_util_clockwise_get(mf)) evas_object_show(wd->front.clip);
207         else evas_object_hide(wd->front.clip);
208      }
209       
210    if (wd->back.content)
211      {
212         evas_map_util_3d_lighting(mb, lx, ly, lz, lr, lg, lb, lar, lag, lab);
213         evas_map_util_3d_perspective(mb, px, py, 0, foc);
214         evas_object_map_set(wd->back.content, mb);
215         evas_object_map_enable_set(wd->back.content, 1);
216         if (evas_map_util_clockwise_get(mb)) evas_object_show(wd->back.clip);
217         else evas_object_hide(wd->back.clip);
218      }
219    
220    evas_map_free(mf);
221    evas_map_free(mb);
222    
223    if (t >= 1.0)
224      {
225         evas_object_map_enable_set(wd->front.content, 0);
226         evas_object_map_enable_set(wd->back.content, 0);
227         wd->animator = NULL;
228         wd->state = !wd->state;
229         _configure(obj);
230         evas_object_smart_callback_call(obj, "animate,done", NULL);
231         return 0;
232      }
233    return 1;
234 }
235
236 static void
237 _configure(Evas_Object *obj)
238 {
239    Widget_Data *wd = elm_widget_data_get(obj);
240    Evas_Coord x, y, w, h;
241    if (!wd) return;
242    evas_object_geometry_get(obj, &x, &y, &w, &h);
243    if (wd->front.content)
244      {
245         if (!wd->animator)
246           evas_object_move(wd->front.content, x, y);
247         evas_object_resize(wd->front.content, w, h);
248      }
249    if (wd->back.content)
250      {
251         if (!wd->animator)
252           evas_object_move(wd->back.content, x, y);
253         evas_object_resize(wd->back.content, w, h);
254      }
255    _flip(obj);
256 }
257
258 static void
259 _move(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
260 {
261    _configure(obj);
262 }
263
264 static void
265 _resize(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
266 {    
267    _configure(obj);
268 }
269
270 static int
271 _animate(void *data)
272 {
273    return _flip(data);
274 }
275
276 /**
277  * Add a new flip to the parent
278  *
279  * @param parent The parent object
280  * @return The new object or NULL if it cannot be created
281  *
282  * @ingroup Flip
283  */
284 EAPI Evas_Object *
285 elm_flip_add(Evas_Object *parent)
286 {
287    Evas_Object *obj;
288    Evas *e;
289    Widget_Data *wd;
290
291    wd = ELM_NEW(Widget_Data);
292    e = evas_object_evas_get(parent);
293    obj = elm_widget_add(e);
294    ELM_SET_WIDTYPE(widtype, "flip");
295    elm_widget_type_set(obj, "flip");
296    elm_widget_sub_object_add(parent, obj);
297    elm_widget_data_set(obj, wd);
298    elm_widget_del_hook_set(obj, _del_hook);
299    elm_widget_theme_hook_set(obj, _theme_hook);
300
301    wd->front.clip = evas_object_rectangle_add(e);
302    evas_object_color_set(wd->front.clip, 255, 255, 255, 255);
303    evas_object_move(wd->front.clip, -49999, -49999);
304    evas_object_resize(wd->front.clip, 99999, 99999);
305    elm_widget_sub_object_add(wd->front.clip, obj);
306    evas_object_smart_member_add(wd->front.clip, obj);
307    
308    wd->back.clip = evas_object_rectangle_add(e);
309    evas_object_color_set(wd->back.clip, 255, 255, 255, 255);
310    evas_object_move(wd->back.clip, -49999, -49999);
311    evas_object_resize(wd->back.clip, 99999, 99999);
312    elm_widget_sub_object_add(wd->back.clip, obj);
313    evas_object_smart_member_add(wd->back.clip, obj);
314
315    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
316    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE, _move, NULL);
317    evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE, _resize, NULL);
318    
319    wd->state = 1;
320      
321    _sizing_eval(obj);
322    return obj;
323 }
324
325 /**
326  * Set the flip front content
327  *
328  * @param obj The flip object
329  * @param content The content to be used in this flip object
330  *
331  * @ingroup Flip
332  */
333 EAPI void
334 elm_flip_content_front_set(Evas_Object *obj, Evas_Object *content)
335 {
336    ELM_CHECK_WIDTYPE(obj, widtype);
337    Widget_Data *wd = elm_widget_data_get(obj);
338    if (!wd) return;
339    if (wd->front.content == content) return;
340    if ((wd->front.content != content) && (wd->front.content))
341      {
342         evas_object_clip_set(wd->front.content, NULL);
343         elm_widget_sub_object_del(obj, wd->front.content);
344         evas_object_smart_member_del(wd->front.content);
345      }
346    wd->front.content = content;
347    if (content)
348      {
349         elm_widget_sub_object_add(obj, content);
350         evas_object_smart_member_add(content, obj);
351         evas_object_clip_set(content, wd->front.clip);
352         evas_object_event_callback_add(content,
353                                        EVAS_CALLBACK_CHANGED_SIZE_HINTS,
354                                        _changed_size_hints, obj);
355         _sizing_eval(obj);
356         if (!elm_flip_front_get(obj)) evas_object_hide(wd->front.clip);
357         else evas_object_show(wd->front.clip);
358      }
359    else
360      evas_object_hide(wd->front.clip);     
361    _configure(obj);
362 }
363
364 /**
365  * Set the flip back content
366  *
367  * @param obj The flip object
368  * @param content The content to be used in this flip object
369  *
370  * @ingroup Flip
371  */
372 EAPI void
373 elm_flip_content_back_set(Evas_Object *obj, Evas_Object *content)
374 {
375    ELM_CHECK_WIDTYPE(obj, widtype);
376    Widget_Data *wd = elm_widget_data_get(obj);
377    if (!wd) return;
378    if (wd->back.content == content) return;
379    if ((wd->back.content != content) && (wd->back.content))
380      {
381         evas_object_clip_set(wd->back.content, NULL);
382         elm_widget_sub_object_del(obj, wd->back.content);
383         evas_object_smart_member_del(wd->back.content);
384      }
385    wd->back.content = content;
386    if (content)
387      {
388         elm_widget_sub_object_add(obj, content);
389         evas_object_smart_member_add(content, obj);
390         evas_object_clip_set(content, wd->back.clip);
391         evas_object_event_callback_add(content,
392                                        EVAS_CALLBACK_CHANGED_SIZE_HINTS,
393                                        _changed_size_hints, obj);
394         _sizing_eval(obj);
395         if (elm_flip_front_get(obj)) evas_object_hide(wd->back.clip);
396         else evas_object_show(wd->back.clip);
397      }
398    else
399      evas_object_hide(wd->back.clip);
400    _configure(obj);
401 }
402
403 /**
404  * Get the flip front content
405  *
406  * @param obj The flip object
407  * @return The content to be used in this flip object front
408  *
409  * @ingroup Flip
410  */
411 EAPI Evas_Object *
412 elm_flip_content_front_get(const Evas_Object *obj)
413 {
414    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
415    Widget_Data *wd = elm_widget_data_get(obj);
416    return wd->front.content;
417 }
418
419 /**
420  * Get the flip back content
421  *
422  * @param obj The flip object
423  * @return The content to be used in this flip object back
424  *
425  * @ingroup Flip
426  */
427 EAPI Evas_Object *
428 elm_flip_content_back_get(const Evas_Object *obj)
429 {
430    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
431    Widget_Data *wd = elm_widget_data_get(obj);
432    return wd->back.content;
433 }
434
435 /**
436  * Get flip front visibility state
437  *
438  * @param obj The flip object
439  * @return If front front is showing or not currently
440  *
441  * @ingroup Flip
442  */
443 EAPI Eina_Bool
444 elm_flip_front_get(const Evas_Object *obj)
445 {
446    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
447    Widget_Data *wd = elm_widget_data_get(obj);
448    if (!wd) return EINA_FALSE;
449    return wd->state;
450 }
451
452 /**
453  * Set flip perspective
454  *
455  * @param obj The flip object
456  * @param foc The coordinate to set the focus on
457  * @param x The X coordinate
458  * @param y The Y coordinate
459  *
460  * NOTE: This function currently does nothing.
461  *
462  * @ingroup Flip
463  */
464 EAPI void
465 elm_flip_perspective_set(Evas_Object *obj, Evas_Coord foc __UNUSED__, Evas_Coord x __UNUSED__, Evas_Coord y __UNUSED__)
466 {
467    ELM_CHECK_WIDTYPE(obj, widtype);
468    Widget_Data *wd = elm_widget_data_get(obj);
469    if (!wd) return;
470 }
471
472 /**
473  * Runs the flip animation
474  *
475  * @param obj The flip object
476  * @param mode The mode type.  Currently accepted modes are:
477  *
478  * ELM_FLIP_ROTATE_Y_CENTER_AXIS
479  * ELM_FLIP_ROTATE_X_CENTER_AXIS
480  * ELM_FLIP_ROTATE_XZ_CENTER_AXIS
481  * ELM_FLIP_ROTATE_YZ_CENTER_AXIS
482  *
483  * @ingroup Flip
484  */
485 EAPI void
486 elm_flip_go(Evas_Object *obj, Elm_Flip_Mode mode)
487 {
488    ELM_CHECK_WIDTYPE(obj, widtype);
489    Widget_Data *wd = elm_widget_data_get(obj);
490    if (!wd) return;
491    if (!wd->animator) wd->animator = ecore_animator_add(_animate, obj);
492    wd->mode = mode;
493    wd->start = ecore_loop_time_get();
494    wd->len = 0.5;
495 }