[evas] Documenting/exemplifying the following:
[framework/uifw/evas.git] / src / examples / evas-smart-object.c
1 /**
2  * Simple Evas example illustrating a custom Evas smart object
3  *
4  * You'll need at least one engine built for it (excluding the buffer
5  * one). See stdout/stderr for output.
6  *
7  * @verbatim
8  * gcc -o evas-smart-object evas-smart-object.c `pkg-config --libs --cflags evas ecore ecore-evas`
9  * @endverbatim
10  */
11
12 #ifdef HAVE_CONFIG_H
13
14 #include "config.h"
15 #else
16
17 #define PACKAGE_EXAMPLES_DIR "."
18 #define __UNUSED__
19
20 #endif
21
22 #include <Ecore.h>
23 #include <Ecore_Evas.h>
24 #include <stdio.h>
25 #include <errno.h>
26
27 #define WIDTH  (320)
28 #define HEIGHT (240)
29
30 static const char *commands = \
31   "commands are:\n"
32   "\tl - insert child rectangle on the left\n"
33   "\tr - insert child rectangle on the right\n"
34   "\tw - remove and delete all members from the smart object\n"
35   "\tright arrow - move smart object to the right\n"
36   "\tleft arrow - move smart object to the left\n"
37   "\tup arrow - move smart object up\n"
38   "\tdown arrow - move smart object down\n"
39   "\td - decrease smart object's size\n"
40   "\ti - increase smart object's size\n"
41   "\tc - change smart object's clipper color\n"
42   "\th - print help\n";
43
44 #define WHITE {255, 255, 255, 255}
45 #define RED   {255, 0, 0, 255}
46 #define GREEN {0, 255, 0, 255}
47 #define BLUE  {0, 0, 255, 255}
48
49 struct test_data
50 {
51    Ecore_Evas  *ee;
52    Evas        *evas;
53    Evas_Object *smt, *bg, *clipper, *rects[2];
54 };
55
56 struct color_tuple
57 {
58    int r, g, b, a;
59 } clipper_colors[4] = {WHITE, RED, GREEN, BLUE};
60 int cur_color = 0;
61
62 static const char *
63 _index_to_color(int i)
64 {
65    switch (i)
66      {
67       case 0:
68         return "WHITE (default)";
69
70       case 1:
71         return "RED";
72
73       case 2:
74         return "GREEN";
75
76       case 3:
77         return "BLUE";
78
79       default:
80         return "other";
81      }
82 }
83
84 static struct test_data d = {0};
85 static const char *border_img_path = PACKAGE_EXAMPLES_DIR "/red.png";
86
87 #define _evas_smart_example_type    "Evas_Smart_Example"
88 #define EVT_CHILDREN_NUMBER_CHANGED "children,changed"
89
90 static const Evas_Smart_Cb_Description _smart_callbacks[] =
91 {
92    {EVT_CHILDREN_NUMBER_CHANGED, "i"},
93    {NULL, NULL}
94 };
95
96 typedef struct _Evas_Smart_Example_Data Evas_Smart_Example_Data;
97 /*
98  * This structure augments clipped smart object's instance data,
99  * providing extra members required by our example smart object's
100  * implementation.
101  */
102 struct _Evas_Smart_Example_Data
103 {
104    Evas_Object_Smart_Clipped_Data base;
105    Evas_Object                   *children[2], *border;
106    int                            child_count;
107 };
108
109 #define EVAS_SMART_EXAMPLE_DATA_GET(o, ptr) \
110   Evas_Smart_Example_Data * ptr = evas_object_smart_data_get(o)
111
112 #define EVAS_SMART_EXAMPLE_DATA_GET_OR_RETURN(o, ptr)        \
113   EVAS_SMART_EXAMPLE_DATA_GET(o, ptr);                       \
114   if (!ptr)                                                  \
115     {                                                        \
116        fprintf(stderr, "No widget data for object %p (%s)!", \
117                o, evas_object_type_get(o));                  \
118        fflush(stderr);                                       \
119        abort();                                              \
120        return;                                               \
121     }
122
123 #define EVAS_SMART_EXAMPLE_DATA_GET_OR_RETURN_VAL(o, ptr, val) \
124   EVAS_SMART_EXAMPLE_DATA_GET(o, ptr);                         \
125   if (!ptr)                                                    \
126     {                                                          \
127        fprintf(stderr, "No widget data for object %p (%s)!",   \
128                o, evas_object_type_get(o));                    \
129        fflush(stderr);                                         \
130        abort();                                                \
131        return val;                                             \
132     }
133
134 EVAS_SMART_SUBCLASS_NEW(_evas_smart_example_type, _evas_smart_example,
135                         Evas_Smart_Class, Evas_Smart_Class,
136                         evas_object_smart_clipped_class_get, _smart_callbacks);
137
138 static void
139 _on_destroy(Ecore_Evas *ee __UNUSED__)
140 {
141    ecore_main_loop_quit();
142 }
143
144 /* here just to keep our example's window size and background image's
145  * size in synchrony */
146 static void
147 _canvas_resize_cb(Ecore_Evas *ee)
148 {
149    int w, h;
150
151    ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
152    evas_object_resize(d.bg, w, h);
153 }
154
155 static void
156 _on_child_del(void        *data,
157               Evas        *evas __UNUSED__,
158               Evas_Object *o,
159               void        *einfo __UNUSED__)
160 {
161    Evas_Object *example_smart = data;
162    int index;
163
164    EVAS_SMART_EXAMPLE_DATA_GET(example_smart, priv);
165
166    index = (int)evas_object_data_get(o, "index");
167    index--;
168
169    priv->children[index] = NULL;
170
171    evas_object_smart_member_del(o);
172    evas_object_smart_changed(example_smart);
173 }
174
175 static void
176 _evas_smart_example_child_callbacks_unregister(Evas_Object *obj)
177 {
178    evas_object_data_set(obj, "index", NULL);
179    evas_object_event_callback_del(obj, EVAS_CALLBACK_FREE, _on_child_del);
180 }
181
182 static void
183 _evas_smart_example_child_callbacks_register(Evas_Object *o,
184                                              Evas_Object *child,
185                                              int          index)
186 {
187    evas_object_event_callback_add(child, EVAS_CALLBACK_FREE, _on_child_del, o);
188    evas_object_data_set(child, "index", (void *)(++index));
189 }
190
191 /* create and setup a new example smart object's internals */
192 static void
193 _evas_smart_example_smart_add(Evas_Object *o)
194 {
195    EVAS_SMART_DATA_ALLOC(o, Evas_Smart_Example_Data);
196
197    /* this is a border around the smart object's area, delimiting it */
198    priv->border = evas_object_image_filled_add(evas_object_evas_get(o));
199    evas_object_image_file_set(priv->border, border_img_path, NULL);
200    evas_object_image_border_set(priv->border, 3, 3, 3, 3);
201    evas_object_image_border_center_fill_set(
202      priv->border, EVAS_BORDER_FILL_NONE);
203    evas_object_smart_member_add(priv->border, o);
204
205    _evas_smart_example_parent_sc->add(o);
206 }
207
208 static void
209 _evas_smart_example_smart_del(Evas_Object *o)
210 {
211    EVAS_SMART_EXAMPLE_DATA_GET(o, priv);
212
213    if (priv->children[0])
214      {
215         _evas_smart_example_child_callbacks_unregister(priv->children[0]);
216         priv->children[0] = NULL;
217      }
218
219    if (priv->children[1])
220      {
221         _evas_smart_example_child_callbacks_unregister(priv->children[1]);
222         priv->children[1] = NULL;
223      }
224
225    _evas_smart_example_parent_sc->del(o);
226 }
227
228 static void
229 _evas_smart_example_smart_show(Evas_Object *o)
230 {
231    EVAS_SMART_EXAMPLE_DATA_GET(o, priv);
232
233    if (priv->children[0]) evas_object_show(priv->children[0]);
234    if (priv->children[1]) evas_object_show(priv->children[1]);
235    evas_object_show(priv->border);
236
237    _evas_smart_example_parent_sc->show(o);
238 }
239
240 static void
241 _evas_smart_example_smart_hide(Evas_Object *o)
242 {
243    EVAS_SMART_EXAMPLE_DATA_GET(o, priv);
244
245    if (priv->children[0]) evas_object_hide(priv->children[0]);
246    if (priv->children[1]) evas_object_hide(priv->children[1]);
247    evas_object_hide(priv->border);
248
249    _evas_smart_example_parent_sc->hide(o);
250 }
251
252 static void
253 _evas_smart_example_smart_resize(Evas_Object *o,
254                                  Evas_Coord   w,
255                                  Evas_Coord   h)
256 {
257    Evas_Coord ow, oh;
258    evas_object_geometry_get(o, NULL, NULL, &ow, &oh);
259    if ((ow == w) && (oh == h)) return;
260
261    /* this will trigger recalculation */
262    evas_object_smart_changed(o);
263 }
264
265 /* act on child objects' properties, before rendering */
266 static void
267 _evas_smart_example_smart_calculate(Evas_Object *o)
268 {
269    Evas_Coord x, y, w, h;
270
271    EVAS_SMART_EXAMPLE_DATA_GET_OR_RETURN(o, priv);
272    evas_object_geometry_get(o, &x, &y, &w, &h);
273
274    evas_object_resize(priv->border, w, h);
275    evas_object_move(priv->border, x, y);
276
277    if (priv->children[0])
278      {
279         evas_object_move(priv->children[0], x + 3, y + 3);
280         evas_object_resize(priv->children[0], (w / 2) - 3, (h / 2) - 3);
281      }
282
283    if (priv->children[1])
284      {
285         evas_object_move(priv->children[1], x + (w / 2), y + (h / 2));
286         evas_object_resize(priv->children[1], (w / 2) - 3, (h / 2) - 3);
287      }
288 }
289
290 /* setting our smart interface */
291 static void
292 _evas_smart_example_smart_set_user(Evas_Smart_Class *sc)
293 {
294    /* specializing these two */
295     sc->add = _evas_smart_example_smart_add;
296     sc->del = _evas_smart_example_smart_del;
297     sc->show = _evas_smart_example_smart_show;
298     sc->hide = _evas_smart_example_smart_hide;
299
300     /* clipped smart object has no hook on resizes or calculations */
301     sc->resize = _evas_smart_example_smart_resize;
302     sc->calculate = _evas_smart_example_smart_calculate;
303 }
304
305 /* BEGINS example smart object's own interface */
306
307 /* add a new example smart object to a canvas */
308 Evas_Object *
309 evas_smart_example_add(Evas *evas)
310 {
311    return evas_object_smart_add(evas, _evas_smart_example_smart_class_new());
312 }
313
314 static void
315 _evas_smart_example_remove_do(Evas_Smart_Example_Data *priv,
316                               Evas_Object             *child,
317                               int                      index)
318 {
319    priv->children[index] = NULL;
320    priv->child_count--;
321    _evas_smart_example_child_callbacks_unregister(child);
322    evas_object_smart_member_del(child);
323 }
324
325 /* remove a child element, return its pointer (or NULL on errors) */
326 Evas_Object *
327 evas_smart_example_remove(Evas_Object *o,
328                           Evas_Object *child)
329 {
330    int index;
331
332    EVAS_SMART_EXAMPLE_DATA_GET_OR_RETURN_VAL(o, priv, NULL);
333
334    if (priv->children[0] != child && priv->children[1] != child)
335      {
336         fprintf(stderr, "You are trying to remove something not belonging to"
337                         " the example smart object!\n");
338         return NULL;
339      }
340
341    index = (int)evas_object_data_get(child, "index");
342    index--;
343
344    _evas_smart_example_remove_do(priv, child, index);
345
346    evas_object_smart_callback_call(
347      o, EVT_CHILDREN_NUMBER_CHANGED, (void *)priv->child_count);
348    evas_object_smart_changed(o);
349
350    return child;
351 }
352
353 /* set to return any previous object set to the left position of the
354  * smart object or NULL, if any (or on errors) */
355 Evas_Object *
356 evas_smart_example_set_left(Evas_Object *o,
357                             Evas_Object *child)
358 {
359    Evas_Object *ret = NULL;
360
361    EVAS_SMART_EXAMPLE_DATA_GET_OR_RETURN_VAL(o, priv, NULL);
362    if (!child)
363      return NULL;
364
365    if (priv->children[1] == child)
366      {
367         fprintf(stderr, "You mustn't place a child on both slots of"
368                         " the example smart object!\n");
369         return NULL;
370      }
371
372    if (priv->children[0])
373      {
374         if (priv->children[0] != child)
375           {
376              ret = priv->children[0];
377              _evas_smart_example_remove_do(priv, priv->children[0], 0);
378           }
379         else return child;
380      }
381
382    priv->children[0] = child;
383    _evas_smart_example_child_callbacks_register(o, child, 0);
384    evas_object_smart_member_add(child, o);
385    evas_object_smart_changed(o);
386
387    priv->child_count++;
388    if (!ret)
389      {
390         evas_object_smart_callback_call(
391           o, EVT_CHILDREN_NUMBER_CHANGED, (void *)priv->child_count);
392      }
393
394    return ret;
395 }
396
397 /* set to return any previous object set to the right position of the
398  * smart object or NULL, if any (or on errors) */
399 Evas_Object *
400 evas_smart_example_set_right(Evas_Object *o,
401                              Evas_Object *child)
402 {
403    Evas_Object *ret = NULL;
404
405    EVAS_SMART_EXAMPLE_DATA_GET_OR_RETURN_VAL(o, priv, NULL);
406    if (!child)
407      return NULL;
408
409    if (priv->children[0] == child)
410      {
411         fprintf(stderr, "You mustn't place a child on both slots of"
412                         " the example smart object!\n");
413         return NULL;
414      }
415
416    if (priv->children[1])
417      {
418         if (priv->children[1] != child)
419           {
420              ret = priv->children[1];
421              _evas_smart_example_remove_do(priv, priv->children[1], 1);
422           }
423         else return child;
424      }
425
426    priv->children[1] = child;
427    _evas_smart_example_child_callbacks_register(o, child, 1);
428    evas_object_smart_member_add(child, o);
429    evas_object_smart_changed(o);
430
431    priv->child_count++;
432    if (!ret)
433      {
434         evas_object_smart_callback_call(
435           o, EVT_CHILDREN_NUMBER_CHANGED, (void *)priv->child_count);
436      }
437
438    return ret;
439 }
440
441 /* END OF example smart object's own interface */
442
443 static void
444 _on_keydown(void        *data __UNUSED__,
445             Evas        *evas __UNUSED__,
446             Evas_Object *o __UNUSED__,
447             void        *einfo)
448 {
449    Evas_Event_Key_Down *ev = einfo;
450
451    if (strcmp(ev->keyname, "h") == 0) /* print help */
452      {
453         fprintf(stdout, commands);
454         return;
455      }
456
457    if (strcmp(ev->keyname, "w") == 0) /* clear out smart object (WRT members) */
458      {
459         if (d.rects[0])
460           {
461              evas_smart_example_remove(d.smt, d.rects[0]);
462              evas_object_del(d.rects[0]);
463           }
464         if (d.rects[1])
465           {
466              evas_smart_example_remove(d.smt, d.rects[1]);
467              evas_object_del(d.rects[1]);
468           }
469
470         memset(d.rects, 0, sizeof(d.rects));
471
472         fprintf(stdout, "Deleting all members of the smart object.\n");
473
474         return;
475      }
476
477    if (strcmp(ev->keyname, "l") == 0) /* insert random colored
478                                        * rectangle on the left */
479      {
480         Evas_Object *rect = evas_object_rectangle_add(d.evas), *prev;
481         evas_object_color_set(
482           rect, rand() % 255, rand() % 255, rand() % 255, 255);
483         evas_object_show(rect);
484
485         prev = evas_smart_example_set_left(d.smt, rect);
486         d.rects[0] = rect;
487
488         fprintf(stdout, "Setting smart object's left spot with a new"
489                         " rectangle.\n");
490         fprintf(stdout, "Checking its new smart object parent: %s\n",
491                 evas_object_smart_parent_get(rect) == d.smt ? "OK!" :
492                 "Failure!");
493         if (prev)
494           {
495              int r, g, b;
496
497              evas_object_color_get(prev, &r, &g, &b, NULL);
498              fprintf(stdout, "Deleting previous left child,"
499                              " which had colors (%d, %d, %d)\n", r, g, b);
500              evas_object_del(prev);
501           }
502
503         return;
504      }
505
506    if (strcmp(ev->keyname, "r") == 0) /* insert random colored
507                                        * rectangle on the right */
508      {
509         Evas_Object *rect = evas_object_rectangle_add(d.evas), *prev;
510         evas_object_color_set(
511           rect, rand() % 255, rand() % 255, rand() % 255, 255);
512         evas_object_show(rect);
513
514         prev = evas_smart_example_set_right(d.smt, rect);
515         d.rects[1] = rect;
516
517         fprintf(stdout, "Setting smart object's right spot with a new"
518                         " rectangle.\n");
519         fprintf(stdout, "Checking its new smart object parent: %s\n",
520                 evas_object_smart_parent_get(rect) == d.smt ? "OK!" :
521                 "Failure!");
522         if (prev)
523           {
524              int r, g, b;
525
526              evas_object_color_get(prev, &r, &g, &b, NULL);
527              fprintf(stdout, "Deleting previous right child,"
528                              " which had colors (%d, %d, %d)\n", r, g, b);
529              evas_object_del(prev);
530           }
531
532         return;
533      }
534
535    /* move smart object along the canvas */
536    if (strcmp(ev->keyname, "Right") == 0 || strcmp(ev->keyname, "Left") == 0 ||
537        strcmp(ev->keyname, "Up") == 0 || strcmp(ev->keyname, "Down") == 0)
538      {
539         Evas_Coord x, y;
540
541         evas_object_geometry_get(d.smt, &x, &y, NULL, NULL);
542
543         switch (ev->keyname[0])
544           {
545            case 'R':
546              x += 20;
547              break;
548
549            case 'L':
550              x -= 20;
551              break;
552
553            case 'U':
554              y -= 20;
555              break;
556
557            case 'D':
558              y += 20;
559              break;
560           }
561
562         evas_object_move(d.smt, x, y);
563
564         return;
565      }
566
567    /* increase smart object's size */
568    if (strcmp(ev->keyname, "i") == 0)
569      {
570         Evas_Coord w, h;
571
572         evas_object_geometry_get(d.smt, NULL, NULL, &w, &h);
573
574         w *= 1.1;
575         h *= 1.1;
576
577         evas_object_resize(d.smt, w, h);
578
579         return;
580      }
581
582    /* decrease smart object's size */
583    if (strcmp(ev->keyname, "d") == 0)
584      {
585         Evas_Coord w, h;
586
587         evas_object_geometry_get(d.smt, NULL, NULL, &w, &h);
588
589         w *= 0.9;
590         h *= 0.9;
591
592         evas_object_resize(d.smt, w, h);
593
594         return;
595      }
596
597    /* change smart object's clipper color */
598    if (strcmp(ev->keyname, "c") == 0)
599      {
600         cur_color = (cur_color + 1) % 4;
601
602         evas_object_color_set(
603           d.clipper, clipper_colors[cur_color].r, clipper_colors[cur_color].g,
604           clipper_colors[cur_color].b, clipper_colors[cur_color].a);
605
606         fprintf (stderr, "Changing clipper's color to %s\n",
607                  _index_to_color(cur_color));
608
609         return;
610      }
611 }
612
613 static void /* callback on number of member objects changed */
614 _on_example_smart_object_child_num_change(void        *data __UNUSED__,
615                                           Evas_Object *obj __UNUSED__,
616                                           void        *event_info)
617 {
618    fprintf(stdout, "Number of child members on our example smart"
619                    " object changed to %d\n", (int)event_info);
620 }
621
622 int
623 main(void)
624 {
625    const Evas_Smart_Cb_Description **descriptions;
626    unsigned int count;
627    Eina_Bool ret;
628
629    srand(time(NULL));
630
631    if (!ecore_evas_init())
632      return EXIT_FAILURE;
633
634    /* this will give you a window with an Evas canvas under the first
635     * engine available */
636    d.ee = ecore_evas_new(NULL, 10, 10, WIDTH, HEIGHT, NULL);
637    if (!d.ee)
638      goto error;
639
640    ecore_evas_callback_destroy_set(d.ee, _on_destroy);
641    ecore_evas_callback_resize_set(d.ee, _canvas_resize_cb);
642    ecore_evas_show(d.ee);
643
644    /* the canvas pointer, de facto */
645    d.evas = ecore_evas_get(d.ee);
646
647    d.bg = evas_object_rectangle_add(d.evas);
648    evas_object_color_set(d.bg, 255, 255, 255, 255);
649    evas_object_move(d.bg, 0, 0);
650    evas_object_resize(d.bg, WIDTH, HEIGHT);
651    evas_object_show(d.bg);
652
653    d.smt = evas_smart_example_add(d.evas);
654    evas_object_move(d.smt, WIDTH / 4, HEIGHT / 4);
655    evas_object_resize(d.smt, WIDTH / 2, HEIGHT / 2);
656    evas_object_show(d.smt);
657
658    ret = evas_object_smart_type_check(d.smt, _evas_smart_example_type);
659    fprintf(stdout, "Adding smart object of type \"%s\" to the canvas: %s.\n",
660            _evas_smart_example_type, ret ? "success" : "failure");
661
662    d.clipper = evas_object_smart_clipped_clipper_get(d.smt);
663    fprintf(stdout, "Checking if clipped smart object's clipper is a "
664                    "\"static\" one: %s\n", evas_object_static_clip_get(
665              d.clipper) ? "yes" : "no");
666
667    evas_object_color_set(
668      d.clipper, clipper_colors[cur_color].r, clipper_colors[cur_color].g,
669      clipper_colors[cur_color].b, clipper_colors[cur_color].a);
670
671    evas_object_smart_callbacks_descriptions_get(
672      d.smt, &descriptions, &count, NULL, NULL);
673
674    for (; *descriptions; descriptions++)
675      {
676         fprintf(stdout, "We've found a smart callback on the smart object!"
677                         "\n\tname: %s\n\ttype: %s\n", (*descriptions)->name,
678                 (*descriptions)->type);
679
680         if (strcmp((*descriptions)->type, "i")) continue;
681         /* we know we don't have other types of smart callbacks
682          * here, just playing with it */
683
684         /* for now, we know the only one callback is the one
685          * reporting number of member objects changed on the
686          * example smart object */
687         evas_object_smart_callback_add(
688           d.smt, (*descriptions)->name,
689           _on_example_smart_object_child_num_change, NULL);
690      }
691
692    evas_object_focus_set(d.bg, EINA_TRUE);
693    evas_object_event_callback_add(
694      d.bg, EVAS_CALLBACK_KEY_DOWN, _on_keydown, NULL);
695
696    fprintf(stdout, commands);
697    ecore_main_loop_begin();
698
699    ecore_evas_free(d.ee);
700    ecore_evas_shutdown();
701    return 0;
702
703 error:
704    fprintf(stderr, "you got to have at least one evas engine built and linked"
705                    " up to ecore-evas for this example to run properly.\n");
706    ecore_evas_shutdown();
707    return -1;
708 }