VII. Introduction▲
Comme dit dans la première partie de l'article, Elementary est une bibliothèque permettant l'exploitation d'une gamme de widgets hautement personnalisables. À l'heure actuelle, Elementary n'est toujours pas sortie en version stable du fait de ses quelques bogues et manques de widgets. Malgré cela, cette bibliothèque reste tout à fait appropriée pour la réalisation d'interfaces graphiques ordinaires.
VII-A. Objectifs▲
Cette partie traitera au mieux l'aspect « C » du développement d'une application avec les EFL. Bien entendu, du code Edje sera introduit afin de montrer comment lier un fichier de description Edje avec du code C, par exemple, ainsi que pour ne pas ignorer la particularité des EFL de séparer la partie graphique de la partie logique d'une application.
Dans le cadre du développement en C, Elementary aura une place centrale mais ne sera logiquement pas la seule bibliothèque utilisée. En effet, Elementary utilisée seule n'est pas suffisante pour écarter les possibilités offertes par d'autres bibliothèques telles qu'Evas, indispensable à la réalisation d'une application, ou Ecore.
VII-B. Compilation▲
La compilation d'un fichier source utilisant Elementary nommé source.c se fait généralement avec la commande suivante :
gcc -o source source.c `pkg-config elementary --cflags --libs`
Si vous obtenez une erreur comme quoi elementary.pc serait introuvable (« Package elementary was not found in the pkg-config search path ») malgré le fait que vous ayez déjà installé correctement Elementary, ajoutez le chemin /.../e17/lib/pkgconfig dans la variable d'environnement PKG_CONFIG_PATH :
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/.../e17/lib/pkgconfig
Tout en remplaçant /.../ par le chemin correct. Cette variable d'environnement a déjà été introduite plus tôt dans l'article, précisée nécessaire dans la partie traitant l'installation des EFL.
Une fois la compilation terminée, on peut trouver l'exécutable à l'emplacement spécifié lors de la compilation.
VIII. Premiers pas avec Elementary▲
VIII-A. Aperçu des widgets d'Elementary▲
Dans le cas où l'on rechercherait un widget ou une personnalisation, Elementary vient avec une application, elementary_test, qui recense un grand nombre de widgets afin de donner un aperçu des différents widgets mis à disposition par la bibliothèque.
L'application est intéressante quand il s'agit de rechercher un widget, mais le code qu'elle met à disposition n'est pas un bon exemple d'écriture de GUI avec les EFL.
VIII-B. Code minimal▲
Quoi de mieux pour commencer l'étude d'une bibliothèque étroitement liée au GUI que de présenter le code minimal d'ouverture d'une fenêtre afin de pouvoir le détailler progressivement par la suite ?
#include <Elementary.h>
static
void
win_del
(
void
*
data, Evas_Object *
obj, void
*
event_info)
{
elm_exit
(
);
}
EAPI int
elm_main
(
int
argc, char
**
argv)
{
Evas_Object *
win;
win =
elm_win_add
(
NULL
, "
main_window
"
, ELM_WIN_BASIC);
evas_object_smart_callback_add
(
win, "
delete,request
"
, win_del, NULL
);
evas_object_show
(
win);
elm_run
(
);
elm_shutdown
(
);
return
EXIT_SUCCESS;
}
ELM_MAIN
(
)
Par le biais de l'inclusion du fichier Elementary.h, nous obtenons la possibilité d'utiliser toutes les fonctions que la bibliothèque met à disposition. Elementary inclut par défaut Eina, Eet, Evas, Ecore et Edje (et d'autres, selon la configuration), ce qui signifie qu'inclure Elementary.h permet d'utiliser toutes ces bibliothèques sans avoir besoin d'inclure des fichiers d'en-tête supplémentaires.
EAPI int
elm_main
(
int
argc, char
**
argv)
{
...
}
ELM_MAIN
(
)
La macro ELM_MAIN() définissant la fonction main() et appelant la fonction elm_main(), cette dernière est considérée comme la fonction principale du programme.
Evas_Object *
win;
Cela définit un pointeur win de type Evas_Object, structure qui correspond à la base de tous les éléments graphiques.
win =
elm_win_add
(
NULL
, "
main_window
"
, ELM_WIN_BASIC);
evas_object_smart_callback_add
(
win, "
delete,request
"
, win_del, NULL
);
evas_object_show
(
win);
L'utilisation de la fonction elm_win_add mène à la création d'une fenêtre de nom unique main_window et de type ELM_WIN_BASIC (une fenêtre classique). L'utilisation - qui sera bien plus amplement détaillée par la suite - de la fonction evas_object_smart_callback_add() fait en sorte que la fonction win_del() soit appelée lorsque l'utilisateur demande à fermer la fenêtre avec le bouton associé. Cette fonction appelle elm_exit() qui permet de quitter la boucle principale, lancée par elm_run(). Enfin, evas_object_show() affiche la fenêtre.
Malgré tout, cette fenêtre n'a pas de titre, contient uniquement un fond noir, et n'est donc au final pas tellement attirante. Remédions-y en changeant le contenu de elm_main() :
EAPI int
elm_main
(
int
argc, char
**
argv)
{
Evas_Object *
win, *
background;
win =
elm_win_add
(
NULL
, "
main_window
"
, ELM_WIN_BASIC);
elm_win_title_set
(
win, "
Elementary
"
);
evas_object_smart_callback_add
(
win, "
delete,request
"
, win_del, NULL
);
background =
elm_bg_add
(
win);
evas_object_size_hint_min_set
(
background, 200
, 100
);
evas_object_size_hint_weight_set
(
background, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
elm_win_resize_object_add
(
win, background);
evas_object_show
(
background);
evas_object_show
(
win);
elm_run
(
);
elm_shutdown
(
);
return
EXIT_SUCCESS;
}
ELM_MAIN
(
)
Par le biais de la fonction elm_win_title_set(), on peut définir le titre d'une fenêtre :
elm_win_title_set
(
win, "
Elementary
"
);
De même, la bibliothèque permet de créer un arrière-plan à un objet Elementary par le biais de la fonction elm_bg_add() :
// Crée un arrière-plan pour win :
background =
elm_bg_add
(
win);
// Définit des dimensions minimales à background :
evas_object_size_hint_min_set
(
background, 200
, 100
);
// Permet un redimensionnement horizontal et vertical :
evas_object_size_hint_weight_set
(
background, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
// Ajoute l'objet dans win et fait en sorte qu'il s'adapte à sa taille :
elm_win_resize_object_add
(
win, background);
evas_object_show
(
background);
La création d'un objet Elementary retourne toujours un Evas_Object*. Il est donc possible de manipuler un tel objet par le biais des méthodes fournies à la fois par la bibliothèque Evas et par la bibliothèque Elementary. Les deux bibliothèques peuvent parfois fournir les mêmes fonctionnalités. Dans ce cas, par convention, il est recommandé d'utiliser les méthodes d'Elementary.
VIII-C. Edje et le code C▲
VIII-C-1. Utiliser un fichier EDJ dans du code C▲
Il est extrêmement rare que des applications complètes soient exclusivement développées en Edje. Comme dit dans la partie précédente, l'intérêt d'Edje est surtout de fournir l'aspect graphique de l'application afin de laisser le C gérer la partie logique. C'est pour cette raison que la méthode de lien d'un fichier Edje avec le code C est abordée ci-dessous, venant avec le concept de layouts :
Evas_Object *
win, *
layout;
...
layout =
elm_layout_add
(
win);
elm_layout_file_set
(
layout, "
interface.edj
"
, "
background_group
"
);
elm_win_resize_object_add
(
win, layout);
evas_object_show
(
layout);
La fonction d'Elementary elm_layout_add() permet de créer un layout à un objet Elementary. Sa syntaxe est identique à celle de elm_bg_add(), vue précédemment. Le reste du code a déjà été vu, à l'exception d'une ligne :
elm_layout_file_set
(
layout, "
interface.edj
"
, "
background_group
"
);
La fonction elm_layout_file_set() permet de définir le fichier EDJ qui sera utilisé en tant que layout. Ici, « background_group » correspond au groupe à charger.
On peut alors manipuler le layout ainsi créé par le biais des fonctions dédiées d'Elementary ainsi qu'avec les fonctions d'Evas.
VIII-C-2. Un exemple▲
Afin d'illustrer l'utilisation d'un fichier EDJ dans du C, prenons un exemple de fichier Edje qui pourra éventuellement servir de base par la suite. C'est d'ailleurs un bel exemple de l'utilisation des macros avec Edje et des éléments de type TABLE. Voici le code complet de chargement avec Elementary :
#include <Elementary.h>
static
void
win_del
(
void
*
data, Evas_Object *
obj, void
*
event_info)
{
elm_exit
(
);
}
EAPI int
elm_main
(
int
argc, char
**
argv)
{
Evas_Object *
win, *
layout;
win =
elm_win_add
(
NULL
, "
main_window
"
, ELM_WIN_BASIC);
elm_win_title_set
(
win, "
Elementary
"
);
evas_object_smart_callback_add
(
win, "
delete,request
"
, win_del, NULL
);
layout =
elm_layout_add
(
win);
elm_layout_file_set
(
layout, "
interface.edj
"
, "
background_group
"
);
elm_win_resize_object_add
(
win, layout);
evas_object_show
(
layout);
evas_object_resize
(
win, 500
, 300
);
evas_object_show
(
win);
elm_run
(
);
elm_shutdown
(
);
return
EXIT_SUCCESS;
}
ELM_MAIN
(
)
Selon les images utilisées, cela ressemblera plus ou moins à ceci :
VIII-C-3. Interactions entre C et EDC▲
L'intérêt premier des fichiers de description Edje est de pouvoir séparer la partie graphique de la partie logique d'une application. Il est ainsi nécessaire de pouvoir gérer les signaux envoyés par Edje avec la partie logique de l'application.
Un programme Edje peut, comme dit dans la partie introduisant Edje, envoyer un signal par le biais de SIGNAL_EMIT :
program {
name
:
"
mouse_click
"
;
signal
:
"
mouse,clicked,1
"
;
source
:
"
element
"
;
action
:
SIGNAL_EMIT "
element,clicked
"
""
;
}
Le signal pourra alors être récupéré par un callback dans le C. Cela sera traité plus en détail dans la partie suivante, concernant les évènements.
Note : comme montré dans la partie précédente de l'article, les fichiers de description Edje peuvent comprendre des widgets d'Elementary avec le type EXTERNAL. C'est pour cette raison qu'il est hautement préférable de définir les widgets Elementary par le biais d'Edje, puis de les gérer dans le code C en récupérant un Evas_Object de l'élément Edje (méthode utilisée dans la partie suivante) puis en l'exploitant afin d'insérer des lignes, du texte ou autre, selon le widget.
VIII-D. La gestion des évènements▲
Un framework ne permettant pas une gestion des évènements est statique et donc peu intéressant. Ainsi, les EFL permettent une gestion des évènements par le biais des callbacks, c'est-à-dire des fonctions qui sont appelées lorsqu'un événement particulier s'est produit. De multiples fonctions sont utilisables selon le contexte pour créer un callback. Parmi elles :
- evas_object_smart_callback_add, utilisée avec des smart objets (ou objets intelligents) ;
- evas_object_event_callback_add, utilisée avec des objets Evas ;
- edje_object_signal_callback_add, quant à elle utilisée pour gérer les signaux en provenance d'une interface Edje.
Le premier moyen a déjà brièvement été présenté dans les exemples. Son prototype est le suivant :
EAPI void
evas_object_smart_callback_add
(
Evas_Object *
obj,
const
char
*
event,
Evas_Smart_Cb func,
const
void
*
data
)
Avec obj, le smart objet sur lequel le callback va s'appliquer, event, l'évènement sur lequel réagir, func, la fonction callback et data, les paramètres à passer à la fonction callback. Cette dernière sera forcément de cette forme :
void
callback
(
void
*
data, Evas_Object *
obj, void
*
event_info) ;
Avec data, la valeur de l'argument data passée lors de l'ajout du callback, obj, l'objet sur lequel l'évènement s'est produit et event_info, le pointeur vers une structure de données pouvant ou non être passé.
Par exemple, pour un bouton Elementary nommé button, on pourrait avoir ceci :
void
button_clicked
(
void
*
data, Evas_Object *
obj, void
*
event_info)
{
printf
(
"
Click event.
\n
"
);
}
...
evas_object_smart_callback_add
(
button, "
clicked
"
, button_clicked, NULL
);
La deuxième méthode est généralement utilisée sur les objets Evas simples :
EAPI void
evas_object_event_callback_add
(
Evas_Object *
obj,
Evas_Callback_Type type,
Evas_Object_Event_Cb func,
const
void
*
data
)
Avec obj, l'objet sur lequel le callback va s'appliquer, type, le type d'évènement appelant le callback (par exemple EVAS_CALLBACK_KEY_DOWN et EVAS_CALLBACK_MOUSE_UP), func, la fonction callback et data, les paramètres à passer à la fonction callback. Cette dernière sera de la forme suivante :
void
callback
(
void
*
data, Evas *
e, Evas_Object *
obj, void
*
event_info);
Avec e, le canvas Evas qui a appelé l'évènement. Les autres arguments sont les mêmes que ceux de la première méthode détaillée. Toutefois, la partie traite d'Elementary et non d'Evas, donc aucune information supplémentaire ne sera donnée concernant cette méthode.
La troisième méthode permet de créer une interaction entre le code C et Edje. Son prototype est le suivant :
EAPI void
edje_object_signal_callback_add
(
Evas_Object *
obj,
const
char
*
emission,
const
char
*
source,
void
(*
)(
void
*
,Evas_Object*
,const
char
*
,const
char
*
) func,
void
*
data
)
Avec obj, l'objet qui va émettre le signal, emission, le nom du signal, source, le nom de la part Edje source, func, la fonction callback et data, les paramètres à passer à la fonction callback, de la forme donnée dans le prototype :
void
callback
(
void
*
data, Evas_Object *
obj, const
char
*
emission, const
char
*
source)
Par exemple, pour récupérer le signal émis dans le programme présenté plus tôt :
void
edje_event
(
void
*
data, Evas_Object *
obj, const
char
*
emission, const
char
*
source)
{
printf
(
"
Click event.
\n
"
);
}
layout =
elm_layout_add
(
win);
elm_layout_file_set
(
layout, "
interface.edj
"
, "
group_name
"
);
elm_win_resize_object_add
(
win, layout);
evas_object_show
(
layout);
edje =
elm_layout_edje_get
(
layout);
edje_object_signal_callback_add
(
edje, "
element,clicked
"
, "
*
"
, edje_event, NULL
);
Il est nécessaire de passer par elm_layout_edje_get() pour récupérer l'objet Edje depuis l'objet Elementary afin d'ajouter par la suite un callback.
Note : on peut également écrire une fonction permettant de récupérer la totalité des évènements se produisant dans l'Edje afin de déboguer :
void
edje_event
(
void
*
data, Evas_Object *
obj, const
char
*
emission, const
char
*
source)
{
printf
(
"
Signal %s from %s
\n
"
, emission, source);
}
...
edje_object_signal_callback_add
(
edje, "
*
"
, "
*
"
, edje_event, NULL
);
Utilisé avec le code d'exemple donné plus tôt dans la partie II.3.2, voici les logs d'un simple clic sur une icône :
Signal mouse,down,1
from table
Signal mouse,down,1
from table[3
]:event_handler
Signal mouse,up,1
from table
Signal mouse,clicked,1
from table
Signal mouse,up,1
from table[3
]:event_handler
Signal mouse,clicked,1
from table[3
]:event_handler
Et dans le cas où l'on aurait ajouté ce programme dans la macro TABLE_IMAGE :
program {
\
name
:
"
mouse_click
"
; \
signal
:
"
mouse,clicked,1
"
; \
source
:
"
event_handler
"
; \
action
:
SIGNAL_EMIT _name"
,clicked
"
""
; \
}
\
Une ligne supplémentaire s'ajouterait aux logs :
Signal graphics,clicked from table[3
]:
L'avantage d'un tel système de logs et que l'on voit l'intégralité de ce qu'il se passe sur l'interface utilisateur, ce qui simplifie grandement le débogage.
IX. Quelques widgets▲
Maintenant que l'on sait comment créer une interface avec Elementary combinée avec Edje ainsi qu'agir en réaction à un événement, il est temps de partir plus en profondeur dans Elementary, et ainsi aller à la rencontre de quelques widgets intéressants.
IX-A. Les listes▲
Les listes sont des éléments extrêmement utilisés dans les diverses applications que l'on utilise quotidiennement, que ce soit pour afficher une liste de fichiers, une playlist ou bien d'autres choses encore. Elementary fournit le widget List pour créer de tels objets dont voici un exemple basique d'utilisation :
Evas_Object *
win, *
list;
char
buf[32
];
int
i;
...
list =
elm_list_add
(
win);
elm_win_resize_object_add
(
win, list);
elm_list_mode_set
(
list, ELM_LIST_LIMIT);
evas_object_size_hint_weight_set
(
list, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
for
(
i =
0
; i <
10
; ++
i)
{
snprintf
(
buf, sizeof
(
buf), "
Élément n°%d
"
, i);
elm_list_item_append
(
list, buf, NULL
, NULL
, NULL
, NULL
);
}
elm_list_go
(
list);
evas_object_show
(
list);
À ce stade de l'article, seules trois fonctions sont à détailler :
- elm_list_mode_set() : permet de changer le mode de la liste ;
- elm_list_item_append() : permet d'ajouter un élément dans la liste ;
- elm_list_go() : à appeler avant d'afficher la liste.
Toutes étant quoi qu'il en soit détaillées dans la documentation.
Cette liste est aussi triste qu'inutile. Il serait intéressant de lui ajouter des icônes sur le côté et de lui assigner une fonction callback afin de gérer les sélections d'éléments.
Le plus simple, les callbacks :
void
list_event
(
void
*
data, Evas_Object *
obj, void
*
event_info)
{
Elm_List_Item *
item;
// On récupère l'item sélectionné de la liste :
item =
elm_list_selected_item_get
(
obj);
// On affiche le message ainsi que le texte de son label :
printf
(
"
Double click happened. %s.
\n
"
, elm_list_item_label_get
(
item));
}
...
evas_object_smart_callback_add
(
list, "
clicked,double
"
, list_event, NULL
);
Lors d'un double-clic sur l'élément n° 5 par exemple, cela affichera ceci dans la console :
Double click happened. Élément n° 5
.
La liste exhaustive des signaux utilisables est disponible dans le fichier elm_list.c.
Ajouter des icônes à une liste est une tâche légèrement plus longue dans le sens où il est nécessaire de créer une boîte pour les afficher.
Evas_Object *
win, *
list, *
box, *
icon;
...
for
(
i =
0
; i <
10
; ++
i)
{
// On crée une boîte :
box =
elm_box_add
(
win);
// On crée une icône :
icon =
elm_icon_add
(
win);
// On définit le fichier image de l'icône :
elm_icon_file_set
(
icon, "
images/icon.png
"
, NULL
);
// On retire la mise à l'échelle :
elm_icon_scale_set
(
icon, 0
, 0
);
// On ajoute l'icône dans la boîte :
elm_box_pack_end
(
box, icon);
// On affiche l'image :
evas_object_show
(
icon);
// Le texte de l'item :
snprintf
(
buf, sizeof
(
buf), "
Élément n°%d
"
, i);
// On utilise la boîte pour afficher l'icône à gauche :
elm_list_item_append
(
list, buf, box, NULL
, NULL
, NULL
);
}
Avec les commentaires, aucune explication additionnelle n'est requise.
IX-B. Les genlist▲
Dans la première partie de l'article, lors de la présentation d'Elementary, le concept des genlists était abordé. Pour résumer l'intérêt, si l'on remplace dans le code précédent la ligne :
for
(
i =
0
; i <
10
; ++
i)
Par la ligne :
for
(
i =
0
; i <
1000
; ++
i)
Que va-t-il se passer ? Pour donner des chiffres, l'application va « juste » passer de 2 Mo à 17 Mo, en termes de consommation de mémoire. Si on rajoute un zéro, faisant passer le nombre d'éléments à 10 000, sous une machine des plus classiques, l'application va se mettre à laguer et va démarrer en plus de vingt secondes. Ainsi, on peut en déduire que le choix d'une liste classique est furieusement inapproprié à une utilisation à grande échelle. C'est là qu'interviennent les genlists, bien plus appropriées à ce type d'usage.
char
*
gl_label_get
(
void
*
data, Evas_Object *
obj, const
char
*
part)
{
char
buf[256
];
snprintf
(
buf, sizeof
(
buf), "
Élément n°%d
"
, (
int
)data);
return
strdup
(
buf);
}
Evas_Object *
gl_icon_get
(
void
*
data, Evas_Object *
obj, const
char
*
part)
{
return
NULL
;
}
Eina_Bool
gl_state_get
(
void
*
data, Evas_Object *
obj, const
char
*
part)
{
return
EINA_FALSE;
}
void
gl_del
(
void
*
data, Evas_Object *
obj)
{
}
...
Evas_Object *
win, *
background, *
genlist;
Elm_Genlist_Item_Class item;
int
i;
genlist =
elm_genlist_add
(
win);
elm_win_resize_object_add
(
win, genlist);
evas_object_size_hint_weight_set
(
genlist, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_show
(
genlist);
item.item_style =
"
default
"
;
item.func.label_get =
gl_label_get;
item.func.icon_get =
gl_icon_get;
item.func.state_get =
gl_state_get;
item.func.del =
gl_del;
for
(
i =
0
; i <
10000
; ++
i)
{
elm_genlist_item_append
(
genlist, &
item, (
void
*
)i,
NULL
, ELM_GENLIST_ITEM_NONE, NULL
, NULL
);
}
evas_object_show
(
genlist);
Le rendu est identique à la capture d'écran présentée plus haut, au détail près qu'il y a bien plus d'éléments.
De manière à avoir un sujet de comparaison avec l'utilisation d'une liste normale, pour dix éléments, l'application va prendre 2.4 Mo et 2.7 Mo pour mille. Avec dix mille éléments, l'application prendra à peine 4 Mo en mémoire (contre environ 150 pour l'utilisation des listes classiques) et ne souffrira d'aucun problème de ralentissement. On peut donc conclure que les genlists sont parfaitement adaptées aux utilisations à grande échelle. Toutefois, comme vous pouvez le voir, le codage d'une genlist est plus complexe que celui d'une simple liste. C'est pour cela qu'il est préférable d'utiliser les listes classiques dans le cas où les éléments à y stocker sont peu nombreux et peu complexes.
Par la notion de complexité, il est entendu que les genlists ne présentent pas uniquement des avantages en matière de consommation en mémoire. Elles sont également pratiques pour réaliser des listes sous forme d'arbre, par exemple. À noter qu'un élément d'une genlist peut être sous plusieurs styles, posséder plusieurs labels, plusieurs états, etc. d'où une réelle divergence des genlists par rapport à leurs cousines.
À première vue, le code donné peut assez logiquement sembler suspect. Pour le comprendre, il est nécessaire d'assimiler précisément la manière dont une genlist fonctionne.
Une genlist crée et détruit les objets des éléments la composant au fur et à mesure que l'utilisateur va la faire défiler, les groupe dans des blocs afin de déterminer aisément lesquels allouer et détruire. Combinée avec un concept de files, une genlist arrive à afficher un nombre incroyable d'éléments avec une consommation ridicule de mémoire. Une question se pose alors : comment fait-elle pour créer et détruire les objets au vol sans stocker les données en mémoire ? C'est là que la structure Elm_Genlist_Item_Class entre en jeu. Son rôle : permettre à une genlist de faire son travail de création/suppression par le biais des différentes fonctions qu'elle fournit.
item.item_style =
"
default
"
;
item.func.label_get =
gl_label_get;
item.func.icon_get =
gl_icon_get;
item.func.state_get =
gl_state_get;
item.func.del =
gl_del;
Il est bien entendu obligatoire de fournir les fonctions, dans le sens où une genlist ne peut pas inventer ce qu'elle doit afficher.
Le concept est assez intéressant. Si vous souhaitez avoir un aperçu de ce qui se fait dans la pratique, soit parce que vous avez du mal à assimiler le concept ou tout simplement par curiosité, créez un int (nommé num, par exemple) et incrémentez-le à chaque fois que la méthode gl_label_get() est appelée. Utilisez alors cet entier pour spécifier le texte de l'élément :
snprintf
(
buf, sizeof
(
buf), "
Élément n°%d
"
, num);
num++
;
return
strdup
(
buf);
Compilez, lancez le programme. Vous pouvez constater que le numéro évolue en permanence, à chaque fois que vous faites défiler la liste.