Bonjour Laurent,
J'ai pris le temps ces derniers jours d'adapter ma bibliothèque pour utiliser TextEntered. Ca marche super bien, et c'est bien plus simple à maintenir que ma solution originale "à la main" ! Par contre, je suis assez embêté par le fait qu'on ne peut récupérer cette information qu'en passant par les événements.
Si j'ai bien compris, j'ai l'heure actuelle deux méthodes différentes pour injecter cet événement dans ma bibliothèque :
- soit je créé ma propre classe de fenêtre qui dérive de sf::Window, et je traite en interne l'événement pour le transférer à ma classe d'input. L'utilisateur doit alors utiliser ma classe de fenêtre au lieu de sf::Window, mais n'a pas d'autre manipulation à faire (je crois que c'est ce que font les autres bibliothèques de GUI que j'ai pu voir par ici).
- soit je décharge la responsabilité à l'utilisateur entièrement, c'est à dire que c'est lui qui, dans sa boucle d'événement, va envoyer l'information à la classe d'input. La manipulation est un peu plus complexe (il ne faut pas se gourer dans l'ordre d'appel des fonctions : envoi de l'événement, mise à jour de la classe d'input, et l'utilisateur peut oublier de le faire), mais pas besoin d'utiliser une classe Window personnalisée.
Comme tu peux le constater, ces deux approches ont des inconvénients, et aucune des deux ne me satisfait vraiment.
Dans le premier cas, je trouve un peu stupide conceptuellement d'avoir une classe "gui::window". Ça donne l'impression que seul un GUI peut exister dans une même fenêtre (ce qui n'est pas le cas), et ça créé un couplage assez fort qui, je pense, n'a pas raison d'être. En effet, si l'utilisateur de ma bibliothèque souhaite utiliser une autre bibliothèque qui a elle aussi sa propre classe "other::window", que doit-il faire ? Créer lui-même son "my::window" qui hérite à la fois de "gui::window" et "other::window" ? :-\ Pour le coup, ça me semble de l'héritage un peu artificiel, conceptuellement bancal, et surtout pas très sympa pour l'utilisateur qui ne devrait pas avoir à se soucier du comportement interne des deux bibliothèques.
Dans le second cas, le problème est évident : je ne veux pas laisser tant de responsabilité à l'utilisateur (c'est pourtant la solution que j'ai choisie, par dépit pour le moment).
Dans tous les cas, je cherche aussi à rendre ma bibliothèque aussi peu invasive que possible (i.e. une fonction update() dans la boucle principale c'est très bien, mais imposer une structure particulière pour transférer un seul événement, c'est bof).
Pour régler mon problème, je te propose deux suggestions différentes :
- intégrer un système de callback à sf::Window. Pour moi c'est l'idéal : je définis mon callback à la création de la classe d'input et c'est fini. C'est d'ailleurs la méthode qui a été retenue pour OIS. L'utilisateur ne voit rien, et tout fonctionne sans qu'il ait à modifier son code d'une quelconque manière. Maintenant, je crois comprendre que, de ton point de vue, soit on mise sur l'event polling (une boucle d'événement unique, et on se débrouille avec ça), soit on mise sur les callbacks, mais pas les deux à la fois. Je me trompe ? Je dois avouer être un peu allergique au concept de boucle d'événement "exclusive" : ça force à écrire du code intrusif et je n'aime pas ça.
- étendre sf::Keyboard pour permettre une gestion "instantanée" (non basée sur les événements) en ajoutant une fonction du style getTextEntered(). L'inconvénient est qu'il faudra probablement ajouter aussi une fonction update() comme pour sf::Joystick (cf. code ci-dessous). Là aussi, je crois que tu n'aimes pas trop ça, vu que ce n'est pas vraiment dans la philosophie de l'input "instantané", et je comprends.
std::u32string Keyboard::getTextEntered() {
return myTextEntered;
}
void Keyboard::update() {
myTextEntered.clear();
// remplir myTextEntered avec les nouveaux caractères qui auront été reçus depuis le dernier appel à update()
}
Qu'en penses-tu ? Y a-t-il de meilleures solutions auxquelles je n'aurais pas pensé ?
PS : dans le même ordre d'idée, on n'a pas accès aux informations sur la molette de la souris dans sf::Mouse. Il faut aussi passer par les événements, avec les mêmes soucis que ceux décrits ci-dessus. Dans ce cas précis tu peux conceptuellement traiter ça comme de l'input instantané en attribuant à la molette la position zéro absolu à l'initialisation, puis en incrémentant cette valeur au gré des mouvements de la molette. En soi cette valeur n'a pas tellement de sens, mais elle permet à quiconque de calculer le "delta" par rapport au dernier appel de la fonction de son côté.