Bienvenue, Invité. Merci de vous connecter ou de vous inscrire.
Avez-vous perdu votre e-mail d'activation ?

Auteur Sujet: lxgui - "Lua and Xml Graphical User Interface"  (Lu 30084 fois)

0 Membres et 1 Invité sur ce sujet

Kalith

  • Jr. Member
  • **
  • Messages: 93
    • Voir le profil
lxgui - "Lua and Xml Graphical User Interface"
« le: Mai 07, 2012, 12:13:53 pm »
Bonjour,

Après avoir codé sur mon temps libre pendant 5 ans, je suis enfin prêt à vous présenter mon plus gros projet : lxgui.


Screenshot de la bibliothèque en action (exemple fourni avec le code source).

Je suis bien conscient qu'il existe déjà des tonnes de bibliothèques qui permettent de gérer un GUI, mais elles ont, je pense, chacune quelque chose qui les rend unique. C'est également le cas pour lxgui. Ses principaux avantages sont :
  • multiplateforme : la bibliothèque est codée en C++ standard (en utilisant les apports du C++11). Les concepts qui dépendent de la plateforme, comme le rendu ou l'input, sont gérés par des plugins (pour le rendu : pour le moment OpenGL pur seulement, pour l'input : SFML ou OIS).
  • extensible : mis à part les composants de base du GUI (gui::frame), toutes les classes "widget" ont étés écrites de façon à se comporter comme des plugins : gui::texture, gui::font_string, gui::button, gui::edit_box, ... L'ajout d'une nouvelle classe est relativement simple et ne nécessite pas de modifier le code source de la bibliothèque.
  • documenté : toutes les classes de la bibliothèque sont documentées. La documentation Doxygen est d’ailleurs inclue avec le code source (elle est également disponible en ligne ici).
  • lecture des données par XML et Lua : il est possible d'utiliser uniquement des fichiers XML (pour structurer le GUI) et des scripts Lua (pour la gestion des événements, etc) pour construire un GUI complet. Il est bien sûr également possible de tout faire en C++.
  • une API familière... : les API XML et Lua sont directement inspirées de celles de World of Warcraft (probablement l'un des meilleurs systèmes que j'ai vu). Il ne s'agit pas d'une simple copie, quelques différences demeurent, mais les fonctionnalités les plus importantes sont là (objets virtuels, héritage, ...).
  • caching : le GUI entier peut être sauvegardé dans une "render target" (pardon pour les anglicismes), de façon à ce que le rendu ne soit effectué que lorsque le GUI est modifié. Ainsi, des interfaces très complexes contenant beaucoup d'objets peuvent être rendues de manière très efficace (en particulier si rien n'est animé, et que le code est basé au maximum sur la réponse aux événements : à titre d'exemple, le screenshot ci-dessus est rendu à 1080 images par secondes quand le "caching" est activé).
J'ai essayé autant que possible de réduire le nombre de dépendances de la bibliothèque, de sorte que la compilation soit la plus simple possible (les fichiers de projet sont inclus pour Code::Blocks, Visual Studio 2010, et CMake). La bibliothèque de GUI en elle même dépend de Lua 5.1 (mais pas 5.2 !) à travers le wrapper C++ "luapp" que j'ai écrit (inclus également). La lecture des fichiers XML est également faite par une bibliothèque de mon cru (elle aussi inclue).
Le seul plugin de rendu actuellement disponible utilise OpenGL (et non SFML, par manque de flexibilité). Il dépend de Freetype pour charger et afficher les polices, ainsi que libpng pour charger les textures (de fait, seuls les fichiers PNG sont supportés).
Pour le plugin d'input, vous pouvez utiliser la SFML2 ou OIS. Malheureusement, puisque la SFML ne propose pas (encore) d'API d'input qui soit indépendante de l'organisation du clavier (AZERTY ou QUERTY par exemple), la classe edit_box ne fonctionnera pas correctement (entre autres) (edit : réglé dans la 1.1.0 !).

Voici une liste des différentes classes qui sont mises à disposition (comme annoncé plus haut, vous pouvez bien sûr en rajouter plein d'autres sans toucher au code de la bibliothèque !) :
  • uiobject (abstrait) : la classe de base. Peut être positionnée à l'écran, et c'est tout.
  • layered_region (abstrait) : peut être rendu à l'écran.
  • frame : peut contenir des layered_regions (triés par calques) ainsi que d'autres frames.
  • texture : peut afficher une texture, un dégradé, ou une simple couleur.
  • font_string : peut afficher du texte.
  • button : une frame sur laquelle ont peut cliquer, et qui possède trois états : "normal", "pushed" et "highligth".
  • check_button : un boutton avec une check box.
  • slider : une frame avec une texture que l'on peut déplacer verticalement ou horizontalement.
  • status_bar : une frame avec une texture qui change de taille d'après une valeur variable (usage typique : barre de santé, ...).
  • edit_box : une boîte de texte éditable (les edit_boxes à plusieurs lignes ne sont pas encore totalement supportés).
  • scroll_frame : une frame avec un contenu déroulable.
Démarrer le GUI en C++ est assez simple (code mis à jour pour la version 1.2.0) :
// On créé une fenêtre SFML
sf::Window mWindow(...);

// On créé un gestionnaire d'input (clavier, souris, ...)
utils::refptr<input::handler_impl> pSFMLHandler(new input::sfml_handler(mWindow));

// On initialise la classe principale gui::manager
gui::manager mManager(
    // On lui donne le gestionnaire d'input
    pSFMLHandler,
    // La langue qui sera utilisée par l'interface
    // (purement informatif : c'est aux addons de se traduire eux-même
    // en utilisant cette valeur)
    "frFR",
    // Les dimensions de la fenêtre de rendu
    mWindow.getSize().x, mWindow.getSize().y,
    // L'implémentation OpenGL du rendu
    utils::refptr<gui::manager_impl>(new gui::gl::manager())
);

// On lit ensuite les fichiers XML et Lua :
//  - d'abord en précisant le dossier dans lequel se trouve le GUI
mManager.add_addon_directory("interface");
//  - puis on créé un contexte Lua
mManager.create_lua([&mManager](){
    // Ce code peut être appelé plus tard, par exemple quand l'utilisateur
    // demande à recharger le GUI (le contexte lua est alors détruit, puis re-créé).
    //  - on spécifie les classes que l'on veut utiliser
    mManager.register_region_type<gui::texture>();
    mManager.register_region_type<gui::font_string>();
    mManager.register_frame_type<gui::button>();
    mManager.register_frame_type<gui::slider>();
    mManager.register_frame_type<gui::edit_box>();
    mManager.register_frame_type<gui::scroll_frame>();
    mManager.register_frame_type<gui::status_bar>();
    //  - on référence dans Lua des fonctions C++ si nécessaire
    // ...
});

//  - puis enfin on charge le tout.
mManager.read_files();

// Dans la boucle principale
while (bRunning)
{
    // On récupère les événements de la fenêtre
    sf::Event mEvent;
    while (mWindow.pollEvent(mEvent))
    {
        // ...

        // On transmet ces événements au gestionnaire d'input
        pSFMLHandler->on_sfml_event(mEvent);
    }

    // On met à jour le GUI
    mManager.update(fDeltaTime);

    // Puis on l'affiche à l'écran
    mManager.render_ui();
}

Avec ces quelques lignes de code, il est possible de créer autant d'addons d'interface en XML et Lua que l'on veut. Considérons un exemple très simple : on veut afficher un compteur qui donne le nombre d'image par seconde en bas à droite de l'écran.
On créé d'abord un nouvel addon, en allant dans le dossier "interface", puis en créant un nouveau dossier que l'on appelle "FPSCounter". Dans ce dossier, on créé un nouveau fichier "table des matières" qui liste tous les fichiers .xml et .lua dont a besoin l'addon, avec quelques autres informations (auteur, version, variables à sauvegarder, ...). Il doit porter le même nom que le dossier, et avoir l'extension ".toc", donc "FPSCounter.toc" :
## Interface: 0001
## Title: Un joli compteur d'IPS
## Version: 1.0
## Author: Kalith
## SavedVariables:

addon.xml

Comme vous le voyez, nous n'allons avoir besoin que d'un seul fichier .xml : "addon.xml". On le créé alors, dans le même dossier. Tout fichier XML doit contenir la balise <Ui> :
<Ui>
</Ui>

Dans cette balise, on créé une frame (qui est donc un genre de conteneur) :
    <Frame name="FPSCounter">
        <Size>
            <RelDimension x="1.0" y="1.0"/>
        </Size>
        <Anchors>
            <Anchor point="CENTER"/>
        </Anchors>
    </Frame>
Cela créé une frame nommée "FPSCounter" et qui rempli tout l'écran : la balise <Size> lui donne une taille relative de "1.0" (relative à son parent, mais comme ici elle n'en a pas, ce sera relatif à la taille de l'écran), et la balise <Anchor> la positionne au milieu de l'écran.
Maintenant, à l'intérieur de cette Frame, on créé un objet FontString pour afficher le texte :
    <Frame name="FPSCounter">
        <Size>
            <RelDimension x="1.0" y="1.0"/>
        </Size>
        <Anchors>
            <Anchor point="CENTER"/>
        </Anchors>
        <Layers><Layer>
            <FontString name="$parentText" font="interface/fonts/main.ttf" text="" fontHeight="12" justifyH="RIGHT" justifyV="BOTTOM" outline="NORMAL">
                <Anchors>
                    <Anchor point="BOTTOMRIGHT">
                        <Offset>
                            <AbsDimension x="-5" y="-5"/>
                        </Offset>
                    </Anchor>
                </Anchors>
                <Color r="0" g="1" b="0"/>
            </FontString>
        </Layer></Layers>
    </Frame>
Nous avons nommé cet objet "$parentText" : "$parent" est alors automatiquement remplacé par le nom de son parent, donc le nom final est "FPSCounterText". Intuitivement, l'attribut "font" décrit quelle police utiliser pour le rendu (fichier .ttf ou .otf), "fontHeight" la taille de la police, "justifyH" et "justifyV" donnent l'alignement horizontal et vertical, et "outline" créé une bordure noire autour du texte, de façon à ce qu'il soit lisible quelque soit l'arrière plan. On le positionne ensuite au coin en bas à droite (BOTTOM-RIGHT) de son parent, avec un petit décalage, et on lui donne une couleur verte.

Maintenant que la structure du GUI est en place, il nous faut toujours calculer et afficher le nombre d'image par seconde. Pour ce faire, on va définir deux "scripts" pour "FPSCounter" :
        <Scripts>
            <OnLoad>
                -- C'est du code Lua !
                self.update_time = 0.5;
                self.timer = 1.0;
                self.frames = 0;
            </OnLoad>
            <OnUpdate>
                -- C'est du code Lua !
                self.timer = self.timer + arg1;
                self.frames = self.frames + 1;

                if (self.timer > self.update_time) then
                    local fps = self.frames/self.timer;
                    self.Text:set_text("FPS : "..fps);
               
                    self.timer = 0.0;
                    self.frames = 0;
                end
            </OnUpdate>
        </Scripts>

Le script "OnLoad" est exécuté une fois et une seule, quand la Frame est créée. On l'utilise ici pour initialiser plusieurs variables. Le script "OnUpdate" est appelé à chaque image du rendu (à utiliser avec précaution, donc ...). Il fournit le temps écoulé depuis le dernier appel dans la variable "arg1". On l'utilise ici pour compter le nombre de mise à jour (donc le nombre d'image) en fonction du temps, et pour mettre à jour notre compteur toute les demi secondes.
La variable "self" en Lua est l'équivalent du "this" du C++ : c'est une référence à "FPSCounter". À noter, puisque l'on a appelé le FontString "$parentText", on peut utiliser un raccourcis d'écriture assez pratique : "self.Text" (au lieu du nom complet "FPSCounterText"), pour faire référence à notre compteur.

Une fois ceci fait, on a le fichier .xml final :
<Ui>
    <Frame name="FPSCounter">
        <Size>
            <RelDimension x="1.0" y="1.0"/>
        </Size>
        <Anchors>
            <Anchor point="CENTER"/>
        </Anchors>
        <Layers><Layer>
            <FontString name="$parentText" font="interface/fonts/main.ttf" text="" fontHeight="12" justifyH="RIGHT" justifyV="BOTTOM" outline="NORMAL">
                <Anchors>
                    <Anchor point="BOTTOMRIGHT">
                        <Offset>
                            <AbsDimension x="-5" y="-5"/>
                        </Offset>
                    </Anchor>
                </Anchors>
                <Color r="0" g="1" b="0"/>
            </FontString>
        </Layer></Layers>
        <Scripts>
            <OnLoad>
                -- C'est du code Lua !
                self.update_time = 0.5;
                self.timer = 1.0;
                self.frames = 0;
            </OnLoad>
            <OnUpdate>
                -- C'est du code Lua !
                self.timer = self.timer + arg1;
                self.frames = self.frames + 1;

                if (self.timer > self.update_time) then
                    local fps = self.frames/self.timer;
                    self.Text:set_text("FPS : "..math.floor(fps));
               
                    self.timer = 0.0;
                    self.frames = 0;
                end
            </OnUpdate>
        </Scripts>
    </Frame>
</Ui>

... et un addon fonctionnel !
Une dernière chose à faire pour pouvoir le voir fonctionner est d'aller dans le dossier "interface", et créer un fichier "addons.txt". Il va contenir la liste des addons à charger. Dans notre cas, on va juste écrire :
FPSCounter:1
Le "1" signifie "à charger". Si l'on met un "0" ou qu'on supprime cette ligne, l'addon ne sera pas chargé.

Faire la même chose en C++ donnerait le code suivant (mis à jour pour la version 1.2.0) :
// On créé la Frame
gui::frame* pFrame = mManager.create_frame<gui::frame>("FPSCounter");
pFrame->set_rel_dimensions(1.0f, 1.0f);
pFrame->set_abs_point(gui::ANCHOR_CENTER, "", gui::ANCHOR_CENTER);

// ... le FontString
gui::font_string* pFont = pFrame->create_region<gui::font_string>(gui::LAYER_ARTWORK, "$parentText");
pFont->set_abs_point(gui::ANCHOR_BOTTOMRIGHT, "$parent", gui::ANCHOR_BOTTOMRIGHT, -5, -5);
pFont->set_font("interface/fonts/main.ttf", 12);
pFont->set_justify_v(gui::text::ALIGN_BOTTOM);
pFont->set_justify_h(gui::text::ALIGN_RIGHT);
pFont->set_outlined(true);
pFont->set_text_color(gui::color::GREEN);
pFont->notify_loaded();

// On créé les scripts en C++ (on pourrait aussi mettre du code Lua ici)
float update_time = 0.5f, timer = 1.0f;
int frames = 0;
pFrame->define_script("OnUpdate",
    [&](gui::frame* self, gui::event* event) {
        float delta = event->get<float>(0);
        timer += delta;
        ++frames;

        if (timer > update_time)
        {
            gui::font_string* text = self->get_region<gui::font_string>("Text");
            text->set_text("FPS : "+utils::to_string(floor(frames/timer)));
           
            timer = 0.0f;
            frames = 0;
        }
    }
);

// On dit à la Frame qu'elle est maintenant chargée.
pFrame->notify_loaded();

Comme vous pouvez le voir sur la capture d'écran ci-dessus, ce système peut être utilisé pour créer des GUI très complexes (le "File selector" est un véritable explorateur de fichiers !). C'est en partie dû à la puissance du système d'héritage (que je n'ai pas présenté ici) : on créé une frame "modèle" (on dit aussi "virtuelle"), qui peut contenir beaucoup d'objets, et avoir beaucoup de propriétés, puis créer plusieurs frames qui suivront ce "modèle" (elles en "héritent"). Cela permet de réduire la quantité de code nécessaire, et peut également vous aider à faire des GUI cohérents : on peut par exemple créer un modèle de bouton que devront utiliser tous les boutons du GUI, de sorte qu'ils aient tous le même aspect.

Je pense clore ici cette (trop longue) présentation. Il n'y a pas de tutoriaux officiels, et pas de documentation pour l'API en Lua, mais vous pouvez vous renseigner sur des sites pour World of Warcraft en attendant (par exemple WoWWiki (en anglais)). J'espère que certains d'entre vous trouveront cette bibliothèque utile !

Dans l'archive du code source, vous trouverez (dans le dossier "gui/test") un programme test qui est sensé compiler et fonctionner si vous avez installé tout correctement. Le rendu doit être identique à la capture d'écran ci-dessus. Ce programme peut aussi être vu comme une démo : vous pouvez y voir comment s'agencent des addons plus compliqués, et quelques exemples d'héritage.

Téléchargements :
Le projet a sa propre page sur sourceforge : ici.
Vous trouvez des archives .zip et .7z contenant l'intégralité des sources, ainsi qu'un dépôt SVN.

Note : la bibliothèque lxgui ainsi que toutes celles qui l'accompagnent sont mise à disposition sous la licence GNU LGPL 3 (vous pouvez utiliser ces bibliothèques sans avoir à divulguer votre code source. En revanche, toute modification faite sur le code source des bibliothèques doit être rendue publique. Voir "gnu.txt" pour plus d'information).
« Modifié: Janvier 25, 2013, 09:24:04 pm par Kalith »
Kal.

Zinlibs

  • Full Member
  • ***
  • Messages: 127
    • Voir le profil
Re : lxgui - "Lua and Xml Graphical User Interface"
« Réponse #1 le: Mai 07, 2012, 02:56:42 pm »
Wow ! Je sais pas trop quoi dire... Magnifique travail !  :o :D
« Modifié: Mai 07, 2012, 02:58:19 pm par Zinlibs »
Zoost & Zoom libraries : An easy way to create and handle geometric objets, animate and use them for better graphics !

Kalith

  • Jr. Member
  • **
  • Messages: 93
    • Voir le profil
Re : lxgui - "Lua and Xml Graphical User Interface"
« Réponse #2 le: Mai 09, 2012, 07:50:05 pm »
Merci, ça fait plaisir :)

J'en profite pour conseiller aux gens qui sont intéressés de favoriser le dépôt SVN au téléchargements directs. Celui-ci contient les toutes dernières mises à jour et corrections de bug (pas de bug constaté sur la version 1.000 pour le moment, mais j'imagine que ça ne saurait tarder).
Kal.

IngloriousTom

  • Newbie
  • *
  • Messages: 17
    • Voir le profil
Re : lxgui - "Lua and Xml Graphical User Interface"
« Réponse #3 le: Mai 17, 2012, 11:10:19 am »
Travail très très intéressant, je risque de m'en servir pour mon prochain projet.

Rexou

  • Newbie
  • *
  • Messages: 9
    • Voir le profil
    • E-mail
Re : lxgui - "Lua and Xml Graphical User Interface"
« Réponse #4 le: Mai 22, 2012, 12:06:13 pm »
Wow, effectivement très beau travail ça force le respect.

Ça doit être sympathique a utiliser, notamment pour éviter la redondance du code, i'll give it a try ;)

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32498
    • Voir le profil
    • SFML's website
    • E-mail
Re : lxgui - "Lua and Xml Graphical User Interface"
« Réponse #5 le: Mai 22, 2012, 12:27:07 pm »
Ca a l'air très abouti, donc j'ai jeté un oeil et voici quelques commentaires et une question.

1. Il est dommage de ne pas avoir utilisé l'une des innombrables bibliothèques open-source disponibles pour le wrapper Lua et le parser XML, d'autant plus que ta licence est compatible avec quasiment toutes. Ces deux aspects représentent un bon paquet de boulot, à la fois en développement initial et en maintenance, ça ne te fait pas peur ? Je pense notamment à tous les cas "à la con" très difficiles à gérer.

2. Ton code Lua intégré à un document XML est incorrect : il faut le mettre dans un bloc CDATA, car il contient des caractères XML (par exemple >). Voilà un exemple typique ce que j'évoque dans le point 1. :P

3. Tu dis que le backend SFML ne peut pas gérer les champs d'édition car la gestion des layouts clavier est mauvaise, je réponds qu'en effet ce point doit être amélioré, mais que tu n'en as pas besoin pour gérer du texte ; il faut taper dans l'évènement TextEntered, qui lui est parfaitement fonctionnel, et non essayer de faire une interprétation perso des touches qui sont enfoncées à un instant T. Là tu t'embêtes pour rien, et tu vas louper plein de façons alternatives d'entrer du texte (par exemple accent puis voyelle pour produire une voyelle accentuée sur un clavier US, ou bien ALT+XXX pour produire un caractère Unicode).

4. J'aimerais grandement savoir ce qu'il manque dans SFML 2 pour que tu puisses implémenter un backend SFML de rendu, la GUI est un cas d'utilisation que je considère important pour le développement de SFML :)

Je me suis aussi permis d'ajouter un peu de coloration syntaxique dans ton post, avec tous ces langages différents c'était dommage que tout reste uniformément gris.
« Modifié: Mai 22, 2012, 12:30:42 pm par Laurent »
Laurent Gomila - SFML developer

Kalith

  • Jr. Member
  • **
  • Messages: 93
    • Voir le profil
Re : lxgui - "Lua and Xml Graphical User Interface"
« Réponse #6 le: Mai 23, 2012, 04:47:49 pm »
Merci d'avoir pris le temps de regarder tout ça ! Je vais répondre à tes remarques une par une.

1. Dans les toutes premières versions, j'utilisais Lua tel quel (appels de fonction C directement donc) et j'avais choisi tinyxml pour parser les fichiers XML.

Du côté de Lua, j'ai petit à petit développé des utilitaires pour simplifier son utilisation dans mon code (donc rien de généraliste), puis ça a fini par se transformer en la bibliothèque luapp qui est maintenant utilisée. Je ne connaissais pas de wrapper C++ à l'époque, donc c'était la voie naturelle.

Du côté de XML, j'ai fini par abandonner tinyxml. C'était il y a déjà quelques années, donc les raisons exactes ne me reviennent plus à l'esprit. Mais je me souviens en tout cas que je n'aimais pas les messages d'erreur qu'il sortait, et qu'il ne fournissait pas de moyen pour vérifier le contenu du fichier (ce qu'on appelle les DTDs, il me semble). Au lieu de repasser à une autre bibliothèque, j'ai eu envie de coder la mienne, pour voir si j'en étais capable et aussi pour m'amuser :)
Le point positif, c'est que je sais ce que cette lib a dans le ventre. En l’occurrence, le XML que j'utilise n'est semble-t-il pas standard, puisque je n'ai aucun support pour les blocs CDATA, et pas la nécessité de gérer plusieurs types d'encodage. De même, les fichiers de validation (.def) ne sont pas du tout standard, et suivent une syntaxe que j'ai complètement inventée mais qui me semble bien plus lisible que ce que j'ai pu voir pour du "vrai" DTD (c'est probablement moins complet, mais largement suffisant pour ce que j'en fait). Un exemple type de fichier .def :
On définit un type de bloc (~ une classe en C++, si on veut) :
<d[Toto]:Region name inherits="" b:virtual="false" b:setAllPoints="false" b:hidden="false">

    <n[.,1]:Size>
        <n[*]:AbsDimension n:x="0"   n:y="0"/>
        <n[*]:RelDimension n:x="1.0" n:y="1.0"/>
    </Size>

    <n[.,1]:Anchors>
        <Anchor point="TOPLEFT" relativeTo="" relativePoint="">
            <n[.,1]:Offset>
                <n[*]:AbsDimension n:x="0"   n:y="0"/>
                <n[*]:RelDimension n:x="0.0" n:y="0.0"/>
            </Offset>
        </Anchor>
    </Anchors>

    <n[1,1]:Regions>
        <l:Region/>
    </Regions>

</Region>
Le "d[Toto]:" devant le nom du bloc signifie "pré-définition", et 'Toto' est le nom d'un bloc dont celui-ci va hériter : si on attend quelque part dans le code un bloc "Toto", alors l'utilisateur pourra donner un bloc "Region" s'il le souhaite.
Les "b:" et "n:" devant le nom d'un attribut signifient qu'on attend respectivement un booléen ou un nombre (par défaut, tout est une chaîne de caractère). Si une valeur est précisée après un attribut, alors il s'agit d'une valeur par défaut, et l'attribut peut ne pas être fourni.
Le "n[X]:" devant le nom d'un sous-bloc précise combien de ces blocs on peut trouver : si X est de la forme "x,y" alors on peut le trouver au moins "x" fois et au maximum "y" fois (un point '.' veut dire "peu importe"), et si X est une étoile '*', alors seul un des blocs en '*' à ce niveau peut être présent.
Enfin, le "l:" devant le nom d'un sous-bloc signifie qu'on charge ("load") ce bloc parmi la liste des blocs pré-définis.

Pour finir le fichier de définition, il faut lui donner la structure du fichier XML, donc son bloc "root", par exemple :
<Ui>
    <Include file/>
    <Script file/>

    <l:Frame/>
</Ui>

Comme tu le dis, ça m'a quand même pris pas mal de temps de coder ces deux bibliothèques utilitaires. Mais d'une c'est déjà fait, et de deux j'y gagne en maîtrise du code. Je sais ce que font ces bibliothèques, et elles ont presque été taillées pour l'usage que j'en fais. Rien ne manque, et il n'y a pas de code ou de fonctionnalité superflus.
Au niveau de la maintenance, je ne me suis pas posé la question. En principe, je ne devrais pas avoir besoin de revoir toute l'architecture de la chose, puisque les besoins étaient posés dès le départ. Maintenant, comme tu l'as fait remarqué (cf ce qui suit), il y a probablement quelques petits soucis ça et là qu'il faut corriger. Mais c'est parfois aussi le cas des bibliothèques plus répandues (même si j'avoue que ce doit être bien plus rare), et dans ces cas là on peut parfois attendre des mois avant que le rapport d'erreur soit lu, puis un mois pour que le correctif soit envoyé sur le dépôt SVN, puis encore un mois pour qu'il arrive dans une version distribuée. Puis combien de temps avant que cette version soit utilisée par la majorité des gens ?
Je trouve quand même la gestion des dépendances extrêmement pénible, autant pour le développeur qui doit faire avec les bugs et changements d'API non prévus, et autant pour les utilisateurs de la bibliothèque qui doivent installer et compiler une centaine de dépendances avant de pouvoir l'utiliser (sous Linux heureusement ce point est relativement négligeable, mais sous Windows...).
Bref : les dépendances, moins j'en ai, mieux je me porte. Même si ça signifie qu'il faut que je ré-invente la moitié de la roue si je n'ai pas besoin du carosse.

2. Là par contre tu as raison : si le contenu d'un bloc de code Lua contient un '<' (l'autre ne gêne pas le parser, mais ce n'est pas une excuse), alors le fichier XML n'est pas lisible. Mais je dois pouvoir régler ça sans avoir recours au bloc CDATA (qui est quand même moche je trouve...), en spécifiant dans mon fichier de définition que les blocs <OnUpdate> etc. ne contiennent que des données et pas d'autres blocs. C'est minimaliste et non standard, mais là encore, bien suffisant pour l'utilisation que j'en fais (à noter : dans World of Warcraft, les blocs de code Lua ne sont pas encadrés par des blocs CDATA non plus, je ne sais pas quelle astuce ils utilisent).

3. Là encore tu as raison. Si la SFML est inutilisable ici, c'est justement parce que la bibliothèque de GUI s'attend à avoir un retour du clavier "non formaté", et elle se charge elle même de traduire les touches en lettres selon la locale qui aura été choisie par l'utilisateur (et contrairement à ce que tu crois, les combinaisons style '^' + 'e' = 'ê' sont bien prises en charge ;) pas les 'Alt' + xxx par contre). Mais comme tu le dis, c'est s'embêter pour rien, puisqu'il vaut mieux que le système gère lui-même cette étape là. Étant donné qu'il n'y a pas de solution portable pour le faire (je n'utilisais pas la SFML au début), j'avais préféré opter pour une solution "à la main", mais portable.
Bref, de mon côté aussi, il y a effectivement du progrès à faire (mais j'aurai quand même besoin d'avoir l'input non formaté, puisque la bibliothèque propose aussi un système de réponse aux touches du clavier, le "key binding", qui lui doit être indépendant du layout utilisé).

4. Du point de vue du rendu, les seules fonctionnalités dont j'ai besoin sont :
  • pouvoir dessiner un quadruplet de vertex arbitraires,
  • pouvoir dessiner un ensemble de tels quadruplets d'un coup (pour le texte : pas obligatoire, mais améliore les performances),
  • pouvoir charger en mémoire un fichier image et manipuler son contenu à la main,
  • pouvoir charger et rasteriser un fichier de police .ttf dans une texture (là je pense que tout le monde utilise Freetype...),
  • et avoir des surfaces de rendu intermédiaires dont on peut modifier les fonctions de blending ("render targets" : pour le caching, mais aussi pour quelques autres utilisations plus exotiques).
Il est bien sûr possible de faire tout ça en OpenGL sans trop se casser la tête, et c'est donc ce que j'ai fait. Avec la SFML 1.6, la classe Sprite était trop restrictive pour la tâche, j'avais donc rajouté un type "Quad" qui permettait de traiter le premier point (... que j'avais d'ailleurs présenté à l'époque sur le forum anglais : click). Mais les VertexArrays semblent (d'après l'API) pouvoir jouer le même rôle, tout en permettant de remplir le deuxième point. Donc a priori de ce côté là, plus de problème !

Par contre j'ai vérifié dans l'API pour les render targets : j'ai besoin de
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
qui n'est pas disponible. En effet, pour rendre correctement des objets transparents avec les render targets, la seule solution exacte est d'utiliser des couleurs en alpha-prémultiplié. Le blend mode que j'utilisais avant (et que tu utilises toi aussi à première vue)
glBlendFuncSeparateEXT(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
est imparfait. Il fonctionne plus ou moins, mais certains cas particuliers rendent extrêmement mal (le texte en petites tailles, bien souvent).
Utiliser de l'alpha-prémultiplié peut sembler un peu effrayant au départ, mais ça ne change pratiquement pas le framework de rendu. Il suffit juste, quand on charge une image, de la parcourir et de multiplier les canaux R, G et B de chaque pixel par le canal alpha. Sans texture, si on veut afficher une sprite rouge à moitié transparente, il suffit de lui donner la couleur (128, 0, 0, 128) au lieu de (255, 0, 0, 128). C'est le prix à payer. Mais on y gagne : la fonction de blending reste
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
et ce, que l'on dessine dans une render target ou à l'écran, et le rendu est parfaitement identique à celui qu'on aurait sans render target. J'ai même lu quelque part que, en évitant une multiplication (GL_ONE au lieu de GL_SRC_ALPHA), on gagne en performance. Mais je n'ai rien mesuré de tel pour le moment.
Par contre j'ai pas encore réfléchi à comment on gérer les autres modes de blending (add, multiply, etc.) dans cette situation...

Voilà pour les réponses ! Autant je pense ne rien changer sur le point 1 sauf faire la correction en 2, autant le point 3 est clairement perfectible. Le point 4 dépend de toi ;)

Merci pour ce retour !
« Modifié: Mai 23, 2012, 04:51:40 pm par Kalith »
Kal.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32498
    • Voir le profil
    • SFML's website
    • E-mail
Re : lxgui - "Lua and Xml Graphical User Interface"
« Réponse #7 le: Mai 23, 2012, 05:01:53 pm »
Citer
Le point positif, c'est que je sais ce que cette lib a dans le ventre. En l’occurrence, le XML que j'utilise n'est semble-t-il pas standard, puisque je n'ai aucun support pour les blocs CDATA, et pas la nécessité de gérer plusieurs types d'encodage.
Oui mais as-tu pensé à la manière dont les gens vont créer ces fichiers XML ? Probablement avec un éditeur XML, qui ne se gênera pas pour sortir divers encodages ou du CDATA.
L'avantage d'utiliser un standard tel que XML est qu'on est de fait compatible avec tous les autres outils basés sur ce standard; or là tu t'engages à mon avis sur une voie qui va te poser des problèmes.

Citer
pouvoir dessiner un quadruplet de vertex arbitraires,
pouvoir dessiner un ensemble de tels quadruplets d'un coup (pour le texte : pas obligatoire, mais améliore les performances),
pouvoir charger en mémoire un fichier image et manipuler son contenu à la main,
pouvoir charger et rasteriser un fichier de police .ttf dans une texture (là je pense que tout le monde utilise Freetype...),
et avoir des surfaces de rendu intermédiaires dont on peut modifier les fonctions de blending ("render targets" : pour le caching, mais aussi pour quelques autres utilisations plus exotiques).
Tout ça est réalisable avec SFML :)

Citer
Il fonctionne plus ou moins, mais certains cas particuliers rendent extrêmement mal (le texte en petites tailles, bien souvent)
Oui effectivement, je m'en suis rendu compte récemment (c'était peut-être toi d'ailleurs ?). Mais je ne comprends pas pourquoi, a priori le résultat devrait être correct avec cette formule. Non ?

Citer
Utiliser de l'alpha-prémultiplié
Quelqu'un avait déjà fait cette requête. J'avoue que je suis assez étranger à cette fonctionnalité, mais je vois que c'est assez utile en fait, donc il n'est pas impossible que ça fasse son chemin dans SFML.

Donc en gros, pour résumer, si SFML supporte l'alpha prémultiplié c'est tout bon pour toi ?
Laurent Gomila - SFML developer

Kalith

  • Jr. Member
  • **
  • Messages: 93
    • Voir le profil
Re : Re : lxgui - "Lua and Xml Graphical User Interface"
« Réponse #8 le: Mai 23, 2012, 07:02:18 pm »
Oui mais as-tu pensé à la manière dont les gens vont créer ces fichiers XML ? Probablement avec un éditeur XML, qui ne se gênera pas pour sortir divers encodages ou du CDATA.
L'avantage d'utiliser un standard tel que XML est qu'on est de fait compatible avec tous les autres outils basés sur ce standard; or là tu t'engages à mon avis sur une voie qui va te poser des problèmes.
Moi même j'utilise un simple éditeur de texte généraliste (geany) : pour ce qu'il y à a faire c'est amplement suffisant. Mais c'est vrai que je n'ai pas testé la compatibilité avec les divers éditeurs spécialisés dans le XML. Ce n'est pas quelque chose que je vise à priori.

Oui effectivement, je m'en suis rendu compte récemment (c'était peut-être toi d'ailleurs ?). Mais je ne comprends pas pourquoi, a priori le résultat devrait être correct avec cette formule. Non ?
J'ai parlé de ce soucis sur plusieurs forums (HGE et Ogre entre autres) mais je n'ai pas souvenir d'en avoir discuté ici.
Je n'arrive plus à retrouver exactement le site sur lequel j'avais été convaincu, celui-ci est cité un peu partout (orienté DirectX, mais on comprend quand même ce qui se passe, cf. la section Compositing Translucent Layers) :
http://home.comcast.net/~tom_forsyth/blog.wiki.html#[[Premultiplied%20alpha]]
mais je ne le trouve pas forcément très clair, et le style est trollesque. Celui-ci est peut être un peu plus clair : click.

Donc en gros, pour résumer, si SFML supporte l'alpha prémultiplié c'est tout bon pour toi ?
En gros, oui :)
Kal.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32498
    • Voir le profil
    • SFML's website
    • E-mail
Re : lxgui - "Lua and Xml Graphical User Interface"
« Réponse #9 le: Mai 23, 2012, 08:25:29 pm »
Citer
Moi même j'utilise un simple éditeur de texte généraliste (geany) : pour ce qu'il y à a faire c'est amplement suffisant. Mais c'est vrai que je n'ai pas testé la compatibilité avec les divers éditeurs spécialisés dans le XML. Ce n'est pas quelque chose que je vise à priori.
Je pense que le tag CDATA, et surtout l'encodage, sont des choses qui vont très vite te poser problème. En l'occurence, des gens vont certainement utiliser de l'UTF-8 (Linux, OS X) ou du Latin-1 (Windows) -- et come il risque d'y avoir des caractères non-ASCII dans ces fichiers (labels, titres, ...) ça posera vraiment problème. D'ailleurs tu gères quoi comme encodage ?

Citer
J'ai parlé de ce soucis sur plusieurs forums (HGE et Ogre entre autres) mais je n'ai pas souvenir d'en avoir discuté ici.
Quelqu'un en avait parlé sur le forum anglais, et avait donné de bons liens aussi ; la conversation ne devrait pas être dure à retrouver si besoin :)

Citer
En gros, oui
Bon, ben c'est pas si dramatique :)
Laurent Gomila - SFML developer

Kalith

  • Jr. Member
  • **
  • Messages: 93
    • Voir le profil
Re : Re : lxgui - "Lua and Xml Graphical User Interface"
« Réponse #10 le: Mai 23, 2012, 09:38:59 pm »
Je pense que le tag CDATA, et surtout l'encodage, sont des choses qui vont très vite te poser problème. En l'occurence, des gens vont certainement utiliser de l'UTF-8 (Linux, OS X) ou du Latin-1 (Windows) -- et come il risque d'y avoir des caractères non-ASCII dans ces fichiers (labels, titres, ...) ça posera vraiment problème.
En principe il ne devrait y avoir aucun caractère spécial dans les fichiers XML. Les noms de Frames sont déjà restreints à être du pur ASCII (puisqu'on doit pouvoir créer une variable du même nom en Lua), la seule chose qui peut éventuellement être non ASCII est le texte affiché à l'écran (et les noms de fichiers pour les textures, techniquement, mais là c'est donner le bâton pour se faire battre). Pour le texte, la bonne pratique est d'utiliser des noms de code qui font référence à une table de localisation, située dans un fichier Lua à part.
Mais dans le fond, rien n'empêche un utilisateur de faire ça dans le fichier XML s'il le veut vraiment.

D'ailleurs tu gères quoi comme encodage ?
Pour le moment, rien ? Le fichier est lu via un std::fstream, sans traitement aucun. À vrai dire, je ne sais pas ce que je pourrais (/devrais) faire de plus. Le seul endroit dans toute la bibliothèque où je me soucie de l'encodage est lorsque je veux afficher du texte à l'écran. Je suppose que le texte récupéré (dans un std::string typiquement) est codé en UTF-8, puis je le convertis en Unicode (std::basic_string<uint>) pour pouvoir utiliser les symboles dessinés par Freetype.

Quelqu'un en avait parlé sur le forum anglais, et avait donné de bons liens aussi ; la conversation ne devrait pas être dure à retrouver si besoin :)
Non effectivement, je suis tombé dessus à plusieurs reprise pendant mes recherches : click.
Kal.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32498
    • Voir le profil
    • SFML's website
    • E-mail
Re : lxgui - "Lua and Xml Graphical User Interface"
« Réponse #11 le: Mai 23, 2012, 11:01:02 pm »
Citer
En principe il ne devrait y avoir aucun caractère spécial dans les fichiers XML. Les noms de Frames sont déjà restreints à être du pur ASCII (puisqu'on doit pouvoir créer une variable du même nom en Lua), la seule chose qui peut éventuellement être non ASCII est le texte affiché à l'écran
Donc... en principe il peut y en avoir ;)

Citer
Pour le texte, la bonne pratique est d'utiliser des noms de code qui font référence à une table de localisation, située dans un fichier Lua à part.
Ca a l'air compliqué. Si XML supporte explicitement différents encodages c'est justement pour ne pas avoir à s'embêter avec ça :P

Citer
Mais dans le fond, rien n'empêche un utilisateur de faire ça dans le fichier XML s'il le veut vraiment.
Et ça va donner quoi une fois que tu l'auras parsé ?

Citer
Pour le moment, rien ? Le fichier est lu via un std::fstream, sans traitement aucun. À vrai dire, je ne sais pas ce que je pourrais (/devrais) faire de plus. Le seul endroit dans toute la bibliothèque où je me soucie de l'encodage est lorsque je veux afficher du texte à l'écran. Je suppose que le texte récupéré (dans un std::string typiquement) est codé en UTF-8, puis je le convertis en Unicode (std::basic_string<uint>) pour pouvoir utiliser les symboles dessinés par Freetype.
Tu vas définitivement avoir de gros problèmes d'encodage. C'est vraiment un sujet compliqué et sensible, et qu'on ne peut malheureusement éviter que quand on joue sur sa propre machine. Mais une fois que des gens vont utiliser ta bibliothèque, ça va te tomber dessus, et ça va faire mal je pense.
Laurent Gomila - SFML developer

Kalith

  • Jr. Member
  • **
  • Messages: 93
    • Voir le profil
Re : Re : lxgui - "Lua and Xml Graphical User Interface"
« Réponse #12 le: Mai 25, 2012, 08:45:11 am »
Donc... en principe il peut y en avoir ;)
Et ça va donner quoi une fois que tu l'auras parsé ?
Aucune idée justement :)

Ca a l'air compliqué. Si XML supporte explicitement différents encodages c'est justement pour ne pas avoir à s'embêter avec ça :P
Pas si compliqué que ça dans le fond. Le fait est que, dans le fichier XML, on ne peut donner qu'une seule valeur pour le texte. Si on veut gérer une application multilingue, le seul meilleur moyen est de ne pas renseigner les attributs XML de texte, puis d'appeler un code Lua dans les scripts <OnLoad> du style :
monObjet:set_text(addon_localization["BUTTON_OK"]);
La table "addon_localization" est remplie par les bonnes traductions selon la locale choisie par l'utilisateur. C'est comme ça que c'est géré dans World of Warcraft d'ailleurs.

Tu vas définitivement avoir de gros problèmes d'encodage. C'est vraiment un sujet compliqué et sensible, et qu'on ne peut malheureusement éviter que quand on joue sur sa propre machine. Mais une fois que des gens vont utiliser ta bibliothèque, ça va te tomber dessus, et ça va faire mal je pense.
Eh bien je ne sais pas trop. J'ai développé cette bibliothèque en parallèle entre Windows XP et Lubuntu, qui en principe gèrent l'encodage différemment si j'ai bien compris ? Pourtant je n'ai jamais eu de problème sur les deux machines. Probablement parce que j'utilise Geany en UTF-8 sur les deux, il faudra que je fasse quelques tests pour voir ce que ça donne en Latin-1.
En tout cas ça ne me semble pas être fou de requérir un encodage particulier pour les fichiers, de la même manière que seules les images PNG sont supportées...
Kal.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32498
    • Voir le profil
    • SFML's website
    • E-mail
Re : lxgui - "Lua and Xml Graphical User Interface"
« Réponse #13 le: Mai 25, 2012, 09:38:17 am »
Citer
Pas si compliqué que ça dans le fond. Le fait est que, dans le fichier XML, on ne peut donner qu'une seule valeur pour le texte. Si on veut gérer une application multilingue, le seul meilleur moyen est de ne pas renseigner les attributs XML de texte, puis d'appeler un code Lua dans les scripts <OnLoad>
En effet le principe n'est pas mauvais, mais tu auras toujours des gens qui ne voudront pas internationaliser leur application et qui définiront directement les textes dans le XML.
Et même si tu définis du texte non-ASCII en Lua, ce sera toujours contenu dans le fichier XML, sauf qu'en plus ce sera interprété par Lua (je ne sais pas de quelle manière) donc tu as encore un niveau supplémentaire d'encodage à maîtriser.
Bref je ne veux pas t'embêter avec ça, c'était juste pour te dire de faire très attention avec les encodages, ça peut vite devenir l'enfer à gérer si tu n'es pas attentif. Souvent ça marche "out of the box" mais ce n'est qu'un coup de chance, et pas représentatif de ce qu'il faut faire pour rendre le code robuste.

Citer
En tout cas ça ne me semble pas être fou de requérir un encodage particulier pour les fichiers, de la même manière que seules les images PNG sont supportées...
Bah, c'était juste pour souligner le fait que tu te limites inutilement et que ça ne plaira peut-être pas à tout le monde. Après à toi de voir ce que tu veux faire :)

Ah par contre pour le PNG, tu peux faire mieux à peu de frais : au lieu de libpng, tu peux utiliser ce que SFML utilise : stb_image. C'est totalement libre (domaine publique) et c'est un seul fichier intégrable directement dans ton code source ; donc pas une vraie dépendance. Et ça gère le png, tga, bmp, jpg, ...
Laurent Gomila - SFML developer

Anata

  • Jr. Member
  • **
  • Messages: 77
    • Voir le profil
Re : lxgui - "Lua and Xml Graphical User Interface"
« Réponse #14 le: Mai 25, 2012, 12:47:04 pm »
Très beau boulot Kalith, franchement félicitation.

Ca fait beaucoup penser a la façon de faire pour les Addons de World Of Warcraft , et c'est pas pour déplaire :D

 

anything