In this example, we illustrate how to create and handle Evas smart objects.
A smart object is one that provides custom functions to handle clipping, hiding, moving, resizing, color setting and more on child elements, automatically, for the smart object's user. They could be as simple as a group of objects that move together (see Clipped Smart Object) or implementations of whole complex UI widgets, providing some intelligence (thus the name) and extension to simple Evas objects.
Here, we create one as an example. What it does is to control (at maximum) 2 child objects, with regard to their geometries and colors. There can be a "left" child and a "right" one. The former will always occupy the top left quadrant of the smart object's area, while the latter will occupy the bottom right. The smart object will also contain an internal decorative border object, which will also be controlled by it, naturally.
a given existing smart class, thus specializing it. This is very common and useful in Evas. There is a built-in smart object, the "clipped smart object", which implements a behavior mostly desired by many other smart object implementors: it will clip its children to its area and move them along with it, on
calls. Then, our example smart object will get that behavior for free.
will define the new smart class' name. The second tells the macro what is the
suffix. On this function, we may override/extend any desired method from our parent smart class:
This code will take place whenever the smart object itself is flagged "dirty", i.e., must be recalculated for rendering (that could come from changes on its clipper, resizing, moving, etc). There, we make sure the decorative border lies on the edges of the smart object and the children, if any, lie on their respective quadrants.
After instantiating our smart object, we do some checks to exemplify some of the API on smart objects:
one will assure we have the string naming our smart class really set to the live object. The
exemplifies usage of "static clippers" – clipped smart objects have their global clippers flagged static.
Here we declare our array of smart callback descriptions, which has one element only, in this case. That callback will take place, as the name indicates, whenever the number of member objects in our smart object example instance changes. That global array variable must be the last argument to EVAS_SMART_SUBCLASS_NEW, so that it's registered as the smart class's callbacks description.
After we instantiate the smart object, we take a look on those descriptions and register a callback on that unique smart event:
The code of the callback will just print how many member objects we have, which is an integer argument of the callback itself, as flagged by its description:
As in other examples, to interact with this one there's a command line interface. A help string can be asked for with the 'h' key:
) spots of our smart object, respectively. The 'w' command will remove all member objects from the smart object and delete them. The keyboard arrows will move the smart object along the canvas. See how it takes any child objects with it during its movement. The 'd' and 'i' keys will increase or decrease the smart object's size – see how it affects the children's sizes, too. Finally, 'c' will change the color of the smart object's clipper (which is the exact internal clipper coming from a clipped smart object):
"Real life" examples of smart objects are Edje and Emotion objects: they both have independent libraries implementing their behavior. The full example follows.
#ifdef HAVE_CONFIG_H
#include "config.h"
#else
#define PACKAGE_EXAMPLES_DIR "."
#define __UNUSED__
#endif
#include <Ecore.h>
#include <Ecore_Evas.h>
#include <stdio.h>
#include <errno.h>
#define WIDTH (320)
#define HEIGHT (240)
static const char *commands = \
"commands are:\n"
"\tl - insert child rectangle on the left\n"
"\tr - insert child rectangle on the right\n"
"\tw - remove and delete all members from the smart object\n"
"\tright arrow - move smart object to the right\n"
"\tleft arrow - move smart object to the left\n"
"\tup arrow - move smart object up\n"
"\tdown arrow - move smart object down\n"
"\td - decrease smart object's size\n"
"\ti - increase smart object's size\n"
"\tc - change smart object's clipper color\n"
"\t. - rotate object to the right\n"
"\t, - rotate object to the left\n"
"\th - print help\n"
"\tq - quit\n"
;
#define WHITE {255, 255, 255, 255}
#define RED {255, 0, 0, 255}
#define GREEN {0, 255, 0, 255}
#define BLUE {0, 0, 255, 255}
struct test_data
{
Ecore_Evas *ee;
};
struct color_tuple
{
int r, g, b, a;
} clipper_colors[4] = {WHITE, RED, GREEN, BLUE};
int cur_color = 0;
int cur_angle = 0;
static const char *
_index_to_color(int i)
{
switch (i)
{
case 0:
return "WHITE (default)";
case 1:
return "RED";
case 2:
return "GREEN";
case 3:
return "BLUE";
default:
return "other";
}
}
static struct test_data d = {0};
static const char *border_img_path = PACKAGE_EXAMPLES_DIR "/red.png";
#define _evas_smart_example_type "Evas_Smart_Example"
#define EVT_CHILDREN_NUMBER_CHANGED "children,changed"
{
{EVT_CHILDREN_NUMBER_CHANGED, "i"},
{NULL, NULL}
};
typedef struct _Evas_Smart_Example_Data Evas_Smart_Example_Data;
struct _Evas_Smart_Example_Data
{
int child_count;
};
#define EVAS_SMART_EXAMPLE_DATA_GET(o, ptr) \
Evas_Smart_Example_Data * ptr = evas_object_smart_data_get(o)
#define EVAS_SMART_EXAMPLE_DATA_GET_OR_RETURN(o, ptr) \
EVAS_SMART_EXAMPLE_DATA_GET(o, ptr); \
if (!ptr) \
{ \
fprintf(stderr, "No widget data for object %p (%s)!", \
o, evas_object_type_get(o)); \
fflush(stderr); \
abort(); \
return; \
}
#define EVAS_SMART_EXAMPLE_DATA_GET_OR_RETURN_VAL(o, ptr, val) \
EVAS_SMART_EXAMPLE_DATA_GET(o, ptr); \
if (!ptr) \
{ \
fprintf(stderr, "No widget data for object %p (%s)!", \
o, evas_object_type_get(o)); \
fflush(stderr); \
abort(); \
return val; \
}
static void
_on_destroy(Ecore_Evas *ee __UNUSED__)
{
ecore_main_loop_quit();
}
static void
_canvas_resize_cb(Ecore_Evas *ee)
{
int w, h;
ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
}
static void
_on_child_del(void *data,
void *einfo __UNUSED__)
{
long idx;
EVAS_SMART_EXAMPLE_DATA_GET(example_smart, priv);
idx--;
priv->children[idx] = NULL;
}
static void
_evas_smart_example_child_callbacks_unregister(
Evas_Object *obj)
{
}
static void
_evas_smart_example_child_callbacks_register(
Evas_Object *o,
long idx)
{
}
static void
{
_evas_smart_example_parent_sc->add(o);
}
static void
{
EVAS_SMART_EXAMPLE_DATA_GET(o, priv);
if (priv->children[0])
{
_evas_smart_example_child_callbacks_unregister(priv->children[0]);
priv->children[0] = NULL;
}
if (priv->children[1])
{
_evas_smart_example_child_callbacks_unregister(priv->children[1]);
priv->children[1] = NULL;
}
_evas_smart_example_parent_sc->del(o);
}
static void
{
EVAS_SMART_EXAMPLE_DATA_GET(o, priv);
_evas_smart_example_parent_sc->show(o);
}
static void
{
EVAS_SMART_EXAMPLE_DATA_GET(o, priv);
_evas_smart_example_parent_sc->hide(o);
}
static void
Evas_Coord w,
Evas_Coord h)
{
Evas_Coord ow, oh;
if ((ow == w) && (oh == h)) return;
}
static void
{
Evas_Coord x, y, w, h;
EVAS_SMART_EXAMPLE_DATA_GET_OR_RETURN(o, priv);
if (priv->children[0])
{
}
if (priv->children[1])
{
}
}
static void
{
sc->
add = _evas_smart_example_smart_add;
sc->
del = _evas_smart_example_smart_del;
sc->
show = _evas_smart_example_smart_show;
sc->
hide = _evas_smart_example_smart_hide;
sc->
resize = _evas_smart_example_smart_resize;
sc->
calculate = _evas_smart_example_smart_calculate;
}
evas_smart_example_add(
Evas *evas)
{
}
static void
_evas_smart_example_remove_do(Evas_Smart_Example_Data *priv,
int idx)
{
priv->children[idx] = NULL;
priv->child_count--;
_evas_smart_example_child_callbacks_unregister(child);
}
{
long idx;
EVAS_SMART_EXAMPLE_DATA_GET_OR_RETURN_VAL(o, priv, NULL);
if (priv->children[0] != child && priv->children[1] != child)
{
fprintf(stderr, "You are trying to remove something not belonging to"
" the example smart object!\n");
return NULL;
}
idx--;
_evas_smart_example_remove_do(priv, child, idx);
o, EVT_CHILDREN_NUMBER_CHANGED, (void *)(long)priv->child_count);
return child;
}
{
EVAS_SMART_EXAMPLE_DATA_GET_OR_RETURN_VAL(o, priv, NULL);
if (!child)
return NULL;
if (priv->children[1] == child)
{
fprintf(stderr, "You mustn't place a child on both slots of"
" the example smart object!\n");
return NULL;
}
if (priv->children[0])
{
if (priv->children[0] != child)
{
ret = priv->children[0];
_evas_smart_example_remove_do(priv, priv->children[0], 0);
}
else return child;
}
priv->children[0] = child;
_evas_smart_example_child_callbacks_register(o, child, 0);
priv->child_count++;
if (!ret)
{
o, EVT_CHILDREN_NUMBER_CHANGED, (void *)(long)priv->child_count);
}
return ret;
}
{
EVAS_SMART_EXAMPLE_DATA_GET_OR_RETURN_VAL(o, priv, NULL);
if (!child)
return NULL;
if (priv->children[0] == child)
{
fprintf(stderr, "You mustn't place a child on both slots of"
" the example smart object!\n");
return NULL;
}
if (priv->children[1])
{
if (priv->children[1] != child)
{
ret = priv->children[1];
_evas_smart_example_remove_do(priv, priv->children[1], 1);
}
else return child;
}
priv->children[1] = child;
_evas_smart_example_child_callbacks_register(o, child, 1);
priv->child_count++;
if (!ret)
{
o, EVT_CHILDREN_NUMBER_CHANGED, (void *)(long)priv->child_count);
}
return ret;
}
static void
_map_update(void)
{
Evas_Coord x, y, w, h;
}
static void
_on_keydown(void *data __UNUSED__,
void *einfo)
{
{
_on_destroy(NULL);
return;
}
{
fprintf(stdout, commands);
return;
}
{
if (d.rects[0])
{
evas_smart_example_remove(d.smt, d.rects[0]);
}
if (d.rects[1])
{
evas_smart_example_remove(d.smt, d.rects[1]);
}
memset(d.rects, 0, sizeof(d.rects));
fprintf(stdout, "Deleting all members of the smart object.\n");
return;
}
{
rect, rand() % 255, rand() % 255, rand() % 255, 255);
prev = evas_smart_example_set_left(d.smt, rect);
d.rects[0] = rect;
fprintf(stdout, "Setting smart object's left spot with a new"
" rectangle.\n");
fprintf(stdout, "Checking its new smart object parent: %s\n",
"Failure!");
if (prev)
{
int r, g, b;
fprintf(stdout, "Deleting previous left child,"
" which had colors (%d, %d, %d)\n", r, g, b);
}
return;
}
{
rect, rand() % 255, rand() % 255, rand() % 255, 255);
prev = evas_smart_example_set_right(d.smt, rect);
d.rects[1] = rect;
fprintf(stdout, "Setting smart object's right spot with a new"
" rectangle.\n");
fprintf(stdout, "Checking its new smart object parent: %s\n",
"Failure!");
if (prev)
{
int r, g, b;
fprintf(stdout, "Deleting previous right child,"
" which had colors (%d, %d, %d)\n", r, g, b);
}
return;
}
if (strcmp(ev->
keyname,
"Right") == 0 || strcmp(ev->
keyname,
"Left") == 0 ||
{
Evas_Coord x, y;
{
case 'R':
x += 20;
break;
case 'L':
x -= 20;
break;
case 'U':
y -= 20;
break;
case 'D':
y += 20;
break;
}
_map_update();
return;
}
{
Evas_Coord w, h;
w *= 1.1;
h *= 1.1;
_map_update();
return;
}
{
Evas_Coord w, h;
w *= 0.9;
h *= 0.9;
_map_update();
return;
}
{
cur_color = (cur_color + 1) % 4;
d.clipper, clipper_colors[cur_color].r, clipper_colors[cur_color].g,
clipper_colors[cur_color].b, clipper_colors[cur_color].a);
fprintf(stderr, "Changing clipper's color to %s\n",
_index_to_color(cur_color));
return;
}
if (strcmp(ev->
keyname,
"period") == 0)
{
cur_angle = (cur_angle + 30) % 360;
_map_update();
return;
}
if (strcmp(ev->
keyname,
"comma") == 0)
{
cur_angle = (cur_angle - 30) % 360;
_map_update();
return;
}
fprintf(stderr,
"Invalid key: '%s'\n", ev->
keyname);
}
static void
_on_example_smart_object_child_num_change(void *data __UNUSED__,
void *event_info)
{
fprintf(stdout, "Number of child members on our example smart"
" object changed to %lu\n", (long)event_info);
}
int
main(void)
{
unsigned int count;
Eina_Bool ret;
srand(time(NULL));
if (!ecore_evas_init())
return EXIT_FAILURE;
d.ee = ecore_evas_new(NULL, 10, 10, WIDTH, HEIGHT, NULL);
if (!d.ee)
goto error;
ecore_evas_callback_destroy_set(d.ee, _on_destroy);
ecore_evas_callback_resize_set(d.ee, _canvas_resize_cb);
ecore_evas_show(d.ee);
d.evas = ecore_evas_get(d.ee);
d.smt = evas_smart_example_add(d.evas);
fprintf(stdout, "Adding smart object of type \"%s\" to the canvas: %s.\n",
_evas_smart_example_type, ret ? "success" : "failure");
fprintf(stdout, "Checking if clipped smart object's clipper is a "
d.clipper) ? "yes" : "no");
d.clipper, clipper_colors[cur_color].r, clipper_colors[cur_color].g,
clipper_colors[cur_color].b, clipper_colors[cur_color].a);
d.smt, &descriptions, &count, NULL, NULL);
for (; *descriptions; descriptions++)
{
fprintf(stdout, "We've found a smart callback on the smart object!"
"\n\tname: %s\n\ttype: %s\n", (*descriptions)->name,
(*descriptions)->type);
if (strcmp((*descriptions)->type, "i")) continue;
d.smt, (*descriptions)->name,
_on_example_smart_object_child_num_change, NULL);
}
fprintf(stdout, commands);
ecore_main_loop_begin();
ecore_evas_free(d.ee);
ecore_evas_shutdown();
return 0;
error:
fprintf(stderr, "you got to have at least one evas engine built and linked"
" up to ecore-evas for this example to run properly.\n");
ecore_evas_shutdown();
return -1;
}