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