Débuter avec les Enlightenment Foundation Libraries (EFL)


précédentsommairesuivant

IV. Introduction à Edje

La description faite dans la partie précédente est suffisante pour comprendre le rôle qu'a Edje dans le développement d'une application avec les EFL. Pour résumer, le principe des EFL est de séparer le design et la partie applicative. Grossièrement dit, on réalise son interface avec Edje sans avoir à écrire la moindre ligne de C puis on conçoit le reste avec Elementary et les autres bibliothèques.

IV-A. Les fichiers EDC et EDJ

La plupart des tutoriels disponibles sur la Toile entendent par Edje deux choses et ce cours n'échappera pas à la règle :

  • les fichiers de description EDC (Edje Data Collection) où est écrite la description du thème de l'application ;
  • les fichiers EDJ obtenus lors de la compilation des fichiers EDC.

Note : c'est la bibliothèque libedje.so qui relie les fichiers EDJ et le code C.

IV-B. Compilation et vision d'un fichier Edje

Dans cette partie, je vais partir du principe que vous avez un fichier description.edc dans un répertoire donné contenant une description de thème quelconque. Si vous souhaitez suivre les étapes de la compilation mais que vous n'avez pas de fichier .edc en attente de compilation, vous pouvez par exemple utiliser l'exemple Hello world donné précédemment :

 
Sélectionnez
collections {
    group {
        name: "interface";
        parts {
            part {
                name: "text";
                type: TEXT;
                description {
                    state: "default" 0.0;
                    color: 255 255 255 255;
                    text {
                        font: "Sans-serif";
                        text: "Hello world !";
                        size: 18;
                    }
                }
            }
        }
    }
}

La compilation d'un fichier Edje est relativement simple. En réalité, cela se limite à une seule commande, qui est la suivante :

 
Sélectionnez
edje_cc description.edc

Avec cela, si aucune erreur n'est détectée, un fichier description.edj a dû faire son apparition dans le même répertoire que le fichier description.edc. Pour visionner un fichier EDJ, on utilise généralement edje_player :

 
Sélectionnez
edje_player description.edj

Cette ligne exécutée, votre interface devrait s'afficher sous vos yeux.

Il est bon de savoir que divers arguments peuvent être passés à l'edje_player et à l'edje_cc pour altérer des choses et d'autres. Pour en connaître la liste, entrez :

 
Sélectionnez
edje_cc --help

Pour edje_cc. Et pour edje_player :

 
Sélectionnez
edje_player --help

V. Écriture d'un fichier de description Edje

V-A. Généralités sur les fichiers EDC

Les fichiers de description Edje, donc les fichiers EDC, font parfois penser à une structure en C ou bien à du code CSS dans le sens où ils correspondent à des définitions de propriétés. On obtient des arbres de définitions de la sorte :

 
Sélectionnez
block
{
    property: value;

    block
    {
        ...
    }

    ...
}

Étudions notre fichier Hello_world.edc contenant le code de l'exemple Hello world.

 
Sélectionnez
collections { ... }

Le mot-clé « collections » correspond à la définition d'un ensemble de groupes définissant le thème de l'application.

 
Sélectionnez
group {
    name: "group_name";
    ...
}

Ce mot-clé-ci correspond quant à lui à la définition d'un groupe, ou objet, composant le thème. Chaque groupe a un nom unique, défini par le mot-clé « name », et peut contenir divers éléments, comme des « parts » ou des scripts (cette notion sera abordée plus loin). Si un groupe du même nom qu'un autre est défini, il va écraser complètement son prédécesseur, d'où l'unicité de la valeur du mot-clé « name ».

 
Sélectionnez
parts {
    part {
    }
    ...
}

Les « parts » sont un ensemble de « part », chacune d'elles représentant les éléments graphiques présents dans un thème, donc un bouton, label, etc.

 
Sélectionnez
part {
    name: "text";
    type: TEXT;
    ...
}

Chaque « part » possède au minimum un nom (name) et un type. Ce dernier peut correspondre à l'un des types suivants :

  • RECT : les rectangles ;
  • TEXT : les textes ;
  • IMAGE : les images ;
  • SWALLOW : les conteneurs ;
  • TEXTBLOCK : les blocs de texte ;
  • GROUP : les groupes ;
  • BOX : les boîtes ;
  • TABLE : les tableaux ;
  • EXTERNAL : les éléments externes.

Dans le cas de l'exemple Hello world, on a un TEXT. Selon le type, la description de chaque part va être menée à varier. Quoi qu'il en soit, les données des propriétés du thème ne sont pas condamnées à garder la valeur qu'on leur a assignée dans le fichier EDC. En effet, le code C généralement situé en parallèle - il est rare de trouver des applications réalisées entièrement avec Edje - fournit bien des moyens de changer leur valeur lors de l'exécution.

Pour plus d'informations concernant les propriétés mises à disposition, vous pouvez consulter cette page de la documentation qui en donne la liste exhaustive. Nous allons tout de même nous pencher par la suite sur plusieurs de ces éléments afin d'illustrer.

Notez que l'on peut commenter les fichiers EDC avec des commentaires tels qu'on les écrit en C :

 
Sélectionnez
// Ceci est un commentaire
/* Ceci en est un autre */

Pour ne pas se perdre dans son travail, tout développeur a l'habitude de créer de multiples fichiers pour aérer son code. Pour cela, Edje permet l'utilisation de directives telles que la directive #include, dont voici un exemple :

 
Sélectionnez
collections { 
    #include "interface.edc" 
    ...
}

V-B. Points particuliers

Maintenant que nous avons passé en revue une description Edje, nous allons pouvoir entrer dans les détails.

V-B-1. Disposition des éléments

Tout d'abord, un point sur l'affichage à l'écran d'un thème : les éléments sont affichés dans l'ordre de leur écriture dans le fichier Edje. Cela signifie que s'il y a lieu de superposition d'éléments, l'élément défini en dernier sera situé « au-dessus » des autres.

Puisque nous parlons de superposition, parlons de disposition : comment faire pour afficher un élément à une position donnée ? Comment faire pour que notre élément s'agrandisse et se rétrécisse selon la taille de la fenêtre ? Comment disposer un élément relativement à un autre ? Le positionnement et le comportement face aux redimensionnements forment sans aucun doute la partie la plus importante d'une interface utilisateur. Edje nous permet par le biais des propriétés des descriptions de la gérer, par de divers moyens.

Dans un premier temps, voyons l'alignement avec les balises « rel1 » et « rel2 ». La balise rel1 correspond au point situé en haut à gauche de l'élément à placer, et rel2 au point en bas à droite. Pour un placement relatif d'un élément à 50 % de l'axe des abscisses et à 0 % de l'axe des ordonnées par rapport à rel1, on va utiliser :

 
Sélectionnez
rel1 {
    relative: 0.5 0.0;
}

Comme vous avez dû le comprendre, 0 % d'un axe va correspondre à la valeur 0 tandis que 100 % va correspondre à 1. Pour un placement absolu à 200x300px de rel1, on va utiliser :

 
Sélectionnez
rel1 { 
    offset: 200 300; 
}

Qui est l'équivalent logique de :

 
Sélectionnez
rel1 { 
    relative: 0 0; 
    offset: 200 300; 
}

Tout en gardant en tête que les valeurs d'offset peuvent être négatives. Il est possible de combiner les propriétés « relative » et « offset ». Cela correspond tout simplement à un positionnement absolu à partir d'un point relatif.

On peut d'ailleurs aussi faire un placement relatif à un autre élément avec l'aide de la propriété « to » :

 
Sélectionnez
rel1 {
    ...
    to: "other part's name";
}

Qui est l'équivalent de :

 
Sélectionnez
rel1 {
    ...
    to_x: "other part's name";
    to_y: "other part's name";
}

Les propriétés « to_x » et « to_y » veulent respectivement dire que l'on souhaite placer notre élément relativement à la position des abscisses (to_x) ou bien des ordonnées (to_y). Utiliser les deux n'est bien entendu pas obligatoire, on peut utiliser l'un et/ou l'autre, ou tout simplement ne pas les utiliser du tout.

Edje laisse au développeur la possibilité de positionner un élément par rapport à la place qui lui est attribuée avec align. Par défaut, lorsqu'un élément est plus petit que l'espace dont il dispose, il va s'y placer au centre (align: 0.5 0.5). Exemple pour placer un élément en haut à gauche de l'espace lui étant alloué :

 
Sélectionnez
align: 0 0; // 0 pour 0 % et 1 pour 100 %

En ce qui concerne les propriétés liées au dimensionnement, on a le choix entre plusieurs éléments. Ci-dessous se trouvent trois des plus importants, en provenance de la documentation :

  • fixed [longueur, 0 ou 1] [hauteur, 0 ou 1] : fixe ou non la longueur et la largeur de l'élément ;
  • min [longueur, px] [hauteur, px] : fixe une taille minimale à l'élément (astuce : -1 pour ne pas fixer, 1 pour laisser Edje calculer la taille) ;
  • max [longueur, px] [hauteur, px] : fixe une taille maximale à l'élément.

Pour le comportement face à un redimensionnement, on va plutôt utiliser aspect, le ratio entre la longueur et la hauteur, et aspect_preference, le comportement de redimensionnement (HORIZONTAL, VERTICAL ou BOTH). Ces deux propriétés sont liées. Exemple d'un rectangle se redimensionnant sur les deux axes tout en conservant ses proportions initiales :

 
Sélectionnez
collections { 
    group { 
        name: "interface"; 
        parts { 
            part { 
                name: "button"; 
                type: RECT; 
                description { 
                    state: "default" 0.0; 
                    aspect: 1.0 1.0; 
                    aspect_preference: BOTH; 
                } 
            } 
        } 
    } 
} 

V-B-2. Les images

La documentation est suffisante pour comprendre comment définir un certain nombre d'éléments. Il peut tout de même s'avérer intéressant de s'arrêter sur les images afin de parler de certaines fonctionnalités en plus du type lui-même.

 
Sélectionnez
collections {
    group {
        name: "interface" ;
        images {
            image: "image_normal.png" COMP;
        }
        parts { 
            part { 
                name: "image"; 
                type: IMAGE; 
                description { 
                    state: "default" 0.0; 
                    aspect: 1 1; 
                    aspect_preference: BOTH; 
                    image { 
                        normal: "image_normal.png"; 
                    } 
                } 
            } 
        } 
    } 
} 

Cela affiche l'image « image_normal.png » s'adaptant à l'écran tout en conservant son ratio d'origine.

 
Sélectionnez
#define IMAGE_NORMAL "image_normal.png" 
collections {
    group {
        name: "interface";
        images {
            image: IMAGE_NORMAL COMP;
        }
        parts { 
            part { 
                name: "image"; 
                type: IMAGE; 
                description { 
                    state: "default" 0.0; 
                    image { 
                        normal: IMAGE_NORMAL; 
                    } 
                } 
            } 
        } 
    } 
} 

Le code identique avec l'utilisation d'une macro IMAGE_NORMAL, dans l'unique but d'informer que les macros existent et s'écrivent tout comme en C.

Afin d'utiliser une image, il est nécessaire de passer au préalable par images pour la définir :

 
Sélectionnez
images { 
    image: "image_normal.png" COMP;
} 

Un certain nombre de formats sont supportés par Edje (JPEG, PNG, etc.), qui permet également une compression de l'objet image. COMP, utilisé dans l'exemple, correspond à une compression sans perte, RAW à aucune compression, LOSSY[x] à une compression de qualité x (x variant entre 0 et 100), comparable à la qualité JPEG, et USER à une image non intégrée au Edje, lue depuis le disque. Il est souvent préférable d'utiliser COMP plutôt que USER dans le sens où il est plus pratique de ne pas avoir à la charger depuis le disque.

Attention : dans le cas de la qualité RAW, il reste nécessaire de faire attention à la taille finale que peut avoir le fichier binaire.

Puisqu'il est question de compression, et donc de qualité, une question se pose : nous avons une image donnée que l'utilisateur peut redimensionner. Comment faire pour qu'elle ne soit pas floue pour les grands écrans ? Une réponse possible serait de joindre une grande image plutôt qu'une image de qualité moyenne. Dans ce cas, la question inverse se poserait : comment faire pour qu'il n'y ait pas de baisse de la qualité pour les petits écrans ? Edje propose une solution : set.

 
Sélectionnez
collections { 
    group { 
        name: "interface"; 
        images { 
            set { 
                name: "my_image"; 
                image { 
                    image: "image-32.png" COMP; 
                    size: 0 0 32 32; 
                } 
                image { 
                    image: "image-64.png" COMP; 
                    size: 33 33 64 64; 
                } 
                image { 
                    image: "image-128.png" COMP; 
                    size: 65 65 128 128; 
                } 
            } 
        } 
        parts { 
            part { 
                name: "image"; 
                type: IMAGE; 
                description { 
                    state: "default" 0.0; 
                    aspect: 1 1; 
                    aspect_preference: BOTH; 
                    image { 
                        normal: "my_image"; 
                    } 
                } 
            } 
        } 
    } 
}

Dans ce code, on définit un set d'images de différentes tailles, avec « my_image » comme valeur de la propriété « name ». Au moment de la définition de l'élément IMAGE, on va utiliser la valeur de la propriété « name » de notre set :

 
Sélectionnez
image { 
    normal: "my_image"; 
} 

De là, Edje va déterminer quelle image choisir selon la taille de la fenêtre à l'exécution à l'aide des propriétés « size » de chaque image du set. Dans le cas où, lors de l'exécution, la taille de la fenêtre ne serait pas incluse dans l'intervalle des propriétés « size », l'image ayant un min-size à 0 sera utilisée jusqu'à la nécessité d'un changement menant à l'utilisation d'une autre image.

La solution des set d'images est très pratique pour éviter d'avoir à passer par une solution bien plus lourde telle que l'utilisation de SVG.

V-B-3. Effets et animations avec Edje

Tout ce qu'on a vu jusque-là est certes assez instructif, mais n'est pas vraiment impressionnant. Cette partie va donc donner l'occasion d'aborder un point que vous attendiez : les animations, les effets, et donc la gestion des évènements, tout cela dans un fichier de description Edje sans la moindre ligne de C.

On appelle « programme » une action effectuée par Edje, généralement en réponse à un signal (un clic de la souris, par exemple). Ces programs peuvent être intégrés à un group et effectuer diverses choses sur divers éléments, comme changer l'état d'un ou plusieurs éléments.

Voyons un exemple simple. On souhaite faire en sorte qu'un élément TEXT affiché à l'écran laisse sa place à un élément IMAGE lorsqu'on clique dessus, et inversement. Ainsi, nous allons devoir définir deux « programmes » : le premier correspondra à la réaction à un clic sur l'élément TEXT et le second à un clic sur l'élément IMAGE.

La méthode n'est pas très compliquée. Chacun des deux éléments, TEXT et IMAGE, va être doté de deux descriptions : une pour l'état par défaut et une autre pour l'état après un clic. Lorsque l'utilisateur va cliquer sur l'élément TEXT, le programme associé à cette interaction va changer l'état des deux éléments à l'état « après un clic » afin de repositionner l'élément TEXT hors de la fenêtre et de repositionner l'élément IMAGE à l'intérieur. Pour un clic sur l'élément IMAGE, l'action est réciproque.

 
Sélectionnez
collections { 
    group { 
        name: "interface"; 
        images { 
            image: "image.png" COMP; 
        } 
        parts { 
            part { // Notre élément TEXT
                name: "text"; 
                type: TEXT; 
                mouse_events: 1; 
                description { // État par défaut
                    state: "default" 0.0; 
                    text { 
                        text: "Cliquez ici !"; 
                        size: 12; 
                        font: "Sans"; 
                    } 
                } 
                description { // État clicked
                    state: "clicked" 0.0; 
                    inherit: "default" 0.0; 
                    rel1.offset: -1000 0; 
                } 
            } 
            part { // Notre élément IMAGE
                name: "image"; 
                type: IMAGE; 
                mouse_events: 1; 
                description { // État par défaut
                    state: "default" 0.0; 
                    max: 128 128; 
                    aspect: 1 1; 
                    aspect_preference: BOTH; 
                    image.normal: "image.png"; 
                    rel1.offset: -1000 0; 
                } 
                description { // État clicked
                    state: "clicked" 0.0; 
                    inherit: "default" 0.0; 
                    rel1.offset: 0 0; 
                } 
            } 
        } 
        programs { 
            program { // Programme déclenché par le TEXT
                name: "text_clicked"; 
                signal: "mouse,clicked,1"; 
                source: "text"; 
                action: STATE_SET "clicked" 0.0; 
                target: "text"; 
                target: "image"; 
            } 
            program { // Programme déclenché par l'IMAGE
                name: "image_clicked"; 
                signal: "mouse,clicked,1"; 
                source: "image"; 
                action: STATE_SET "default" 0.0; 
                target: "text"; 
                target: "image"; 
            } 
        } 
    } 
}

Avec la propriété mouse_events mise à 0, on dit qu'on n'accepte pas les événements de la souris, tandis qu'avec cette même propriété mise à 1, on dit qu'on les accepte. C'est pour cela que nos deux éléments ont la propriété définie à 1 :

 
Sélectionnez
mouse_events: 1; // On accepte les évènements de la souris

Note : par défaut, la propriété mouse_events est déjà à 1. L'intérêt de la définir à 1 dans le code n'a donc qu'une utilité, vous avertir que cela existe.

L'utilisation de la propriété inherit sur les descriptions clicked de nos éléments avec pour argument default est un moyen d'éviter de devoir écrire deux fois la même chose. Cela permet de dire à Edje « cette description a les mêmes propriétés et valeurs que la description default ». À partir de là, il ne nous reste plus qu'à placer les propriétés divergeant de l'état par défaut :

 
Sélectionnez
description { // Description de 
    state: "clicked" 0.0; // État clicked
    inherit: "default" 0.0; // Hérite de default
    rel1.offset: 0 0; // On change uniquement le placement
} 

Dans chacun de nos programmes, nous définissons :

  • name, le nom du programme ;
  • signal, le signal qui va déclencher le programme ;
  • source, le nom de l'élément source du signal (au plus, une seule source par programme) ;
  • action, l'action à effectuer lors du déclenchement du programme ;
  • target, la cible de l'action (il peut y avoir plusieurs cibles).
 
Sélectionnez
program { // Programme déclenché par le TEXT
    name: "text_clicked"; 
    signal: "mouse,clicked,1"; // Appelé par un clic de la souris
    source: "text"; // En provenance de l'élément TEXT
    action: STATE_SET "clicked" 0.0; // Change l'état à clicked
    target: "text"; // ... pour le TEXT
    target: "image"; // ... et pour l'IMAGE
} 

Si vous testez ce code, vous vous rendez compte que l'image apparaît immédiatement après un clic sur l'élément TEXT, qui va quant à lui disparaître. Avec la propriété transition de chaque programme, on va pouvoir passer d'un simple effet à une petite animation, et cela en une seule ligne.

La propriété transition a la syntaxe suivante :

 
Sélectionnez
transition: TYPE paramètre;

Avec TYPE faisant partie des types de transition suivants :

  • NONE ;
  • LINEAR ;
  • SINUSOIDAL ;
  • ACCELERATE ;
  • DECELERATE ;

Et paramètre correspondant au temps de transition en secondes. Plus la valeur passée sera élevée, plus l'animation sera longue.

Par exemple, si on met pour chacun de nos programmes :

 
Sélectionnez
transition: ACCELERATE 1;

On pourra voir une animation lors d'un clic sur l'image ou le texte. L'élément présent va sortir de l'écran en l'espace d'une seconde avec une vitesse s'accélérant, puis l'élément non présent va entrer dans l'écran par un effet identique.

Note : les actions des programmes ne se limitent pas aux simples changements d'état. À l'heure où j'écris cet article, un programme peut également envoyer un signal pouvant par exemple être capté et géré par le code C (nous verrons cela par la suite), ainsi que stopper l'exécution d'un autre programme.

Consulter la documentation des programmes Edje.

V-B-4. Aperçu des scripts avec Embryo

Notez avant tout que l'on peut aussi bien écrire des scripts Edje avec Embryo qu'avec du code LUA.

Edje est basé sur Evas pour la gestion des éléments graphiques, sur Ecore pour la gestion de tout ce qui concerne la boucle évènementielle et Eet pour tout ce qui concerne les opérations core. Il peut également utiliser Embryo pour utiliser les scripts dans une description de thème. Les scripts Edje fonctionnent en continuité avec les programmes. En réalité, ce sont les programmes qui les appellent par le biais de leur propriété script.

Afin de donner un aperçu des scripts avec Embryo dans un fichier de description Edje, on va remodeler l'exemple précédent pour appeler les programmes non pas avec la méthode énoncée plus haut, mais plutôt avec un script.

 
Sélectionnez
collections { 
    group { 
        images { 
            image: "image.png" COMP; 
        } 
        name: "interface"; 
        script { // Notre script
            public callPrograms() { 
                new buf[16]; 
                new Float:val; 
                get_state(PART:"text", buf, sizeof(buf), val); // Récupération de l'état dans buf
                if(!strcmp(buf, "default")) { // Si l'état de notre TEXT est default
                    run_program(PROGRAM:"hide_text"); // Appel du programme hide_text
                    run_program(PROGRAM:"show_image"); // Appel du programme show_image
                } 
                // Sinon :
                else { 
                    run_program(PROGRAM:"show_text"); // Appel du programme show_text
                    run_program(PROGRAM:"hide_image"); // Appel du programme hide_image
                } 
            } 
        } 
        parts { 
            part { 
                name: "text"; 
                type: TEXT; 
                mouse_events: 0; 
                description { 
                    state: "default" 0.0; 
                    text { 
                        text: "Cliquez ici !"; 
                        size: 12; 
                        font: "Sans"; 
                    } 
                } 
                description { 
                    state: "hidden" 0.0; 
                    inherit: "default" 0.0; 
                    rel1.offset: -1000 0; 
                } 
            } 
            part { 
                name: "image"; 
                type: IMAGE; 
                mouse_events: 0; 
                description { 
                    state: "default" 0.0; 
                    max: 128 128; 
                    aspect: 1 1; 
                    aspect_preference: BOTH; 
                    image.normal: "image.png"; 
                    rel1.offset: -1000 0; 
                } 
                description { 
                    state: "shown" 0.0; 
                    inherit: "default" 0.0; 
                    rel1.offset: 0 0; 
                } 
            } 
            part { 
                name: "event_handler"; 
                type: RECT; 
                mouse_events: 1; 
                description { 
                    state: "default" 0.0; 
                    color: 0 0 0 0; 
                } 
            } 
        } 
        programs { 
            program { 
                name: "show_text"; 
                action: STATE_SET "default" 0.0; 
                transition: ACCELERATE 1; 
                target: "text"; 
            } 
            program { 
                name: "hide_text"; 
                action: STATE_SET "hidden" 0.0; 
                transition: ACCELERATE 1; 
                target: "text"; 
            } 
            program { 
                name: "hide_image"; 
                action: STATE_SET "default" 0.0; 
                transition: ACCELERATE 1; 
                target: "image"; 
            } 
            program { 
                name: "show_image"; 
                action: STATE_SET "shown" 0.0; 
                transition: ACCELERATE 1; 
                target: "image"; 
            } 
            program { 
                name: "event_clicked"; 
                signal: "mouse,clicked,1"; 
                source: "event_handler"; 
                script { 
                    callPrograms(); 
                } 
            } 
        } 
    } 
}

Pour simplifier la gestion des signaux, on a utilisé un élément RECT invisible dont le rôle est de récupérer les signaux qui nous intéressent - ici, le signal clicked en provenance d'un clic de souris. À savoir que cette manière de récupérer les signaux est très utilisée. Par conséquent, nos éléments TEXT et IMAGE n'ont pas besoin de récupérer les évènements de la souris :

 
Sélectionnez
mouse_events: 0;

On peut noter que le code des scripts ressemble furieusement à du code C tel qu'on a l'habitude de l'écrire. Toutefois, la liste des fonctions pouvant être utilisées est assez restreinte. La documentation d'Embryo étant également assez limitée au moment où l'article est en train d'être écrit, la solution pour accéder à une liste exhaustive de ces fonctions est d'aller directement consulter les sources, donc le fichier edje_embryo.c.

Pour ce type d'usage, il n'est pas nécessaire d'utiliser un script. Par contre, dès qu'il s'agit de vérifier un état avec get_state, de définir le texte d'un élément avec set_text, etc. sans pourtant vouloir passer par du code C, les scripts forment une solution idéale.

VI. Un minijeu en Edje combiné avec Elementary

Afin de valider les connaissances acquises, d'utiliser les scripts Embryo de manière plus concrète et d'introduire le type d'élément EXTERNAL, la réalisation d'un minijeu est une idée judicieuse. Le jeu qui sera codé est un jeu de « Plus ou Moins », qui consiste à générer un nombre aléatoire et de le faire deviner à l'utilisateur, en lui donnant les indications plus ou moins.

Image non disponible

Pour annoncer que l'on va utiliser Elementary et utiliser certains de ses widgets directement dans un fichier de description Edje, on doit passer par une déclaration d'externals.

 
Sélectionnez
externals { // On précise qu'on va utiliser Elementary : 
    external: "elm"; 
}

À partir de là, on peut créer des parts de type EXTERNAL, comme un bouton :

 
Sélectionnez
part { // Déclaration d'un bouton Elementary
    name: "button"; 
    type: EXTERNAL; 
    source: "elm/button"; 
    description { 
        state: "default" 0.0; 
        params { 
            string: "label" "Cliquez sur moi !"; 
        } 
    } 
}

Les scripts Embryo peuvent alors gérer ces éléments par le biais des fonctions spécialement conçues pour les éléments du type étudié. Ces fonctions sont de la forme external_param_set_**** et external_param_get_****. Les arguments qu'elles prennent et les valeurs de retour peuvent être trouvées dans les sources, donc dans le fichier edje_embryo.c, comme le reste des fonctions d'Embryo disponibles dans Edje. Par exemple, pour récupérer le nombre affiché dans un widget spinner (le widget affichant 16 dans la capture d'écran, et possédant des boutons pour altérer cette valeur), on va utiliser le code suivant :

 
Sélectionnez
new Float:num = external_param_get_float(PART:"form", "value");

Avec l'élément form, un widget spinner d'Elementary, et value le paramètre du spinner qui contient la valeur indiquée dans le widget. Pour savoir quels paramètres on peut récupérer, l'idéal est d'aller dans les sources, donc dans {Chemin vers les sources des EFL}/elementary/src/edje_externals/. Dans ce dossier, vous trouverez des fichiers nommés elm_{nom du widget}.c qui contiennent la liste des paramètres utilisables ainsi que les types. Si on ouvre elm_spinner.c, on peut trouver cette liste :

 
Sélectionnez
static Edje_External_Param_Info external_spinner_params[] = { 
   DEFINE_EXTERNAL_COMMON_PARAMS, 
   EDJE_EXTERNAL_PARAM_INFO_STRING_DEFAULT("label format", "%1.2f"), 
   EDJE_EXTERNAL_PARAM_INFO_DOUBLE("min"), 
   EDJE_EXTERNAL_PARAM_INFO_DOUBLE_DEFAULT("max", 100.0), 
   EDJE_EXTERNAL_PARAM_INFO_DOUBLE_DEFAULT("step", 1.0), 
   EDJE_EXTERNAL_PARAM_INFO_DOUBLE("value"), 
   EDJE_EXTERNAL_PARAM_INFO_BOOL("wrap"), 
   EDJE_EXTERNAL_PARAM_INFO_SENTINEL 
};

Pour pouvoir réaliser le minijeu, une dernière question se pose : comment générer le nombre aléatoire ? Embryo propose deux fonctions de génération, rand() et randf() qui sont tout à fait adaptées au besoin et que l'on peut utiliser un peu comme en C. Par exemple :

 
Sélectionnez
script {
    public rand_num; // Nombre aléatoire 
    public generate() { // Génère un nombre aléatoire 
        set_int(rand_num, rand()%25); // On utilise set_int pour stocker le nombre généré 
    } 
}

Désormais, vous avez tous les éléments nécessaires à la réalisation du minijeu ; tenter de le faire serait un bon moyen de vous entrainer. Quoi qu'il en soit, voici une proposition parmi bien d'autres de code, commenté pour simplifier la compréhension :

 
Sélectionnez
collections { 
    group { 
        name: "interface"; 
        min: 200 175; 

        externals { // On précise qu'on va utiliser Elementary : 
            external: "elm"; 
        } 
        script { // Le script de notre minijeu 
            public rand_num; // Nombre à trouver 
            public startGame() { // Génère le nombre à trouver 
                set_int(rand_num, rand()%25); 
            } 
            public checkNum() { // Plus ? Moins ? Gagné ? 
                new Float:num = external_param_get_float(PART:"form", "value"); 
                if (num > get_int(rand_num)) // Moins ! 
                    external_param_set_str(PART:"info", "label", "C'est moins !"); 
                else if (num < get_int(rand_num)) // Plus ! 
                    external_param_set_str(PART:"info", "label", "C'est plus !"); 
                else // Gagné ! 
                    external_param_set_str(PART:"info", "label", "Vous avez gagné !"); 
            } 
        } 
        parts { 
            part { // Un fond blanc 
                name: "background"; 
                type: RECT; 
                mouse_events: 0; 
                description { 
                    state: "default" 0.0; 
                    color: 255 255 255 255; 
                } 
            } 
            part { // Label de titre 
                name: "text"; 
                type: EXTERNAL; 
                source: "elm/label"; 
                description { 
                    state: "default" 0.0; 
                    align: 0 0; 
                    min: 0 0; 
                    rel1 { 
                        offset: 15 5; 
                    } 
                    params { 
                        string: "label" "Insérez un nombre entre 0 et 25 :"; 
                    } 
                } 
            }
            part { // Formulaire 
                name: "form"; 
                type: EXTERNAL; 
                source: "elm/spinner"; 
                description { 
                    state: "default" 0.0; 
                    align: 0 0; 
                    max: 100 1; 
                    rel1 { 
                        to_y: "text"; 
                        relative: 0.0 1.0; 
                        offset: 25 15; 
                    } 
                    params { 
                        double: "min" 0; 
                        double: "max" "25"; 
                        double: "value" "0"; 
                    } 
                } 
            } 
            part { // Bouton de validation du formulaire 
                name: "button"; 
                type: EXTERNAL; 
                source: "elm/button"; 
                description { 
                    state: "default" 0.0; 
                    align: 0 0; 
                    max: 0 1; 
                    rel1 { 
                        to_y: "form"; 
                        relative: 0.0 1.0; 
                        offset: 25 5; 
                    } 
                    params { 
                        string: "label" "Tester le nombre !"; 
                    } 
                } 
            } 
            part { // Label indiquant plus, moins, etc. 
                name: "info"; 
                type: EXTERNAL; 
                source: "elm/label"; 
                description { 
                    state: "default" 0.0; 
                    align: 0 0; 
                    min: 0 0; 
                    rel1 { 
                        to_y: "button"; 
                        relative: 0.0 1.0; 
                        offset: 15 15; 
                   } 
                   params { 
                       string: "label" ""; 
                   } 
                } 
            } 
        } 
        programs { 
            program { // On a cliqué sur le bouton 
                name: "click_event"; 
                signal: "clicked"; 
                source: "button"; 
                script { 
                    checkNum(); 
                } 
            } 
            program { // Début de la partie 
                name: "load_event"; 
                signal: "load"; 
                script { 
                    startGame(); 
                } 
            } 
        } 
    } 
}

Ce code peut bien entendu être amélioré, par exemple en ajoutant une gestion du nombre de coups, d'une difficulté, etc. N'hésitez surtout pas à vous attarder sur ce minijeu pour y apporter vos retouches, c'est essentiel pour prendre la main efficacement.


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 2011 Louis du Verdier. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.