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

Auteur Sujet: SimpleGui  (Lu 6583 fois)

0 Membres et 1 Invité sur ce sujet

Cmdu76

  • Full Member
  • ***
  • Messages: 194
    • Voir le profil
SimpleGui
« le: Août 10, 2014, 09:24:47 pm »
SimpleGui:

SimpleGui est une bibliothèque de GUI pour SFML ayant pour but de gérer les GUI "principaux".
Je me suis inspiré de quelques autres systèmes (https://github.com/Lo-X/potato-framework et https://github.com/SFML/SFML-Game-Development-Book (merci à leurs auteurs))
Mais contrairement à ses systèmes, je voulais que la gestion des évènements se fassent à partir de la souris

On distingue deux types d'objets différents, les Widgets et les Components. Les Widgets sont des GUI et les Components permettent de créer les Widgets plus facilement.

Liste des Widgets :
  • Container : Il va contenir les autres widgets pour les manipuler facilement, il peut être charger depuis un fichier
  • Button : C'est un bouton, il gère 3 états de Sprite différent, la gestion du Callback, et un texte
  • LineEdit : C'est une case qui permet d'écrire du texte et elle gère aussi 3 états de Sprite différent et un Callback
  • Label : C'est juste un texte volant
  • LabelBox : C'est un texte contenu dans une Sprite (à 2 état)
  • CheckBox : C'est une boîte cochable (à 3 états)
  • CheckBoxText : C'est une boîte cochable (à 3 états) et un texte
  • RadioBox : Ce sont des CheckBox mais une seule RadioBox peut être cochée à la fois
  • RadioBoxText : Ce sont des CheckBoxText mais une seule RadioBoxText peut être cochée à la fois
  • Slider : C'est une case qui permet de faire varier une valeur entre un intervalle en déplacant un curseur. Un Slider a 3 états et un Callback
  • ...
  • ... Grâce au système de Components il est facile de créer ses propres Widgets, vous pouvez donc en proposer d'autres ...

Pour obtenir un exemple, c'est ici : https://github.com/Cmdu76/SimpleGui/blob/master/src/main.cpp
Pour obtenir les sources, c'est ici : https://github.com/Cmdu76/SimpleGui/

Si vous avez des critiques, des suggestions ou des remarques, je suis preneur :)
« Modifié: Août 17, 2014, 02:02:00 am par Cmdu76 »

DridriLaBastos

  • Newbie
  • *
  • Messages: 2
    • Voir le profil
Re : SimpleGui
« Réponse #1 le: Août 11, 2014, 06:36:06 pm »
L'idée est sympas, j'avais déjà fait un bouton à l'époque où j'étais sous windows mais je n'ai jamais pensé à aller plus loin, il faudrait que je m'y remette si j'ai le temp, tu me donnes envi... :)
Simple question cependant :  st il possible d'envoyer une fonction au constructeur de ton bouton (ou autrement) si l'on veut qu'il fasse quelque chose lorsque l'on appuie dessus?

Cmdu76

  • Full Member
  • ***
  • Messages: 194
    • Voir le profil
Re : SimpleGui
« Réponse #2 le: Août 11, 2014, 10:17:30 pm »
Oui il est tout à fait possible de faire ça :)

Il faut utiliser la fonction Button::setCallback()

Voici un exemple de la gestion d'un bouton, tiré de l'exemple que je donne plus haut :

auto get = std::make_shared<SGUI::Button>(font,texture);
get->setTexture(texture);
get->setFont(font);
get->setString("Get");
get->setPosition(50, 250);
get->setTextureRect(SGUI::Button::Normal, sf::IntRect(0,0,200,50));
get->setTextureRect(SGUI::Button::Active, sf::IntRect(0,50,200,50));
get->setTextureRect(SGUI::Button::Disabled, sf::IntRect(0,100,200,50));
get->setCallback([&] ()
{
    label->setString(line->getString());
});

Ici, un clic sur le bouton entraîne donc la modification du texte d'un Label avec le texte contenu dans une LineEdit, ce qui est plutôt pratique :)

Voilà, si tu as d'autres questions ou suggestions, n'hésite pas ;)

TheKingArthur

  • Invité
Re : SimpleGui
« Réponse #3 le: Août 13, 2014, 12:24:16 am »
J'aime bien le principe. J'ai voulu aussi créer des widgets de ce type car je trouve que la SFML manque de fonctionnalités à ce sujet.
Peut-être l'utiliserais-je.

En tout cas c une bonne idée.

Lolilolight

  • Hero Member
  • *****
  • Messages: 1232
    • Voir le profil
Re : SimpleGui
« Réponse #4 le: Août 15, 2014, 11:22:18 am »
Salut.

Les std::make_shared c'est bien mais que se passerait t'il si on a envie de cloner un bouton et si en plus la classe bouton dérive de la classe Widget ???
A tu prévus un fonction pour ???

Ne vaudrait t'il pas mieux partir sur un design de ce genre ?


class BaseInterface {
     public :
     void addChild(Widget* w) {
        children.push_back(w);
     }
     virtual
     std::vector<Widget*> getChildren() {
        return children;
     }
     std::vector<Widget*> children;
};
template <typename T>
class Interface : public BaseInterface  {
    public:        using DataType = T;
        DataType /* const */ & data() /* const*/{return t_;}

    protected:
        Interface(T & t) : t_(t){}
        const T& clone(const T& i) {
                   const T* c = new T(i);
                   return *c;
        }
        virtual  ~Interface(){}

        //~Interface(){}
    private:
        T & t_;
};
class Widget : public BaseInterface {
    //Définition de la classe.
    virtual void exampleMethod (BaseInterface& bi) {
    for (unsigned int i = 0; i < children.size(); i++) {
           children[i]->exampleMethod(bi);
    }
};
class Button : public Widget, public Interface<Button> {
    void exampleMethod(BaseInterface& bi) {
             static_cast<Interface<Button>&>(bi).data().exampleMethod().exampleMethod(*this);
    }
    void exampleMethod(Button &button) {
             //Methode definition here.
    }
};
 

En principe tu as 2 interfaces : une de base et une dérivée, ta classe de base hérite de l'interface de base et ta classe dérivée de l'interface dérivée.

Tu peux, dans ton interface de base si tu le veux, définir des widgets enfants, si une action s'applique à un widget parent tu pourra la rappliquer sur les widgets enfants de cette manière.

Dans l'interface enfants tu définis les méthodes de bases des widgets, ici je n'ai défini que la méthode clone, ça évite de devoir définir une méthode clone pour chaque type de widget.

Pour appeler une méthode et la rappliquer sur les widgets enfants, il suffit de définit une méthode dans la classe Widget qui va itérer sur tout les widget enfants, cette méthode doit être virtuelle.

Et dans chaque sous classes, tu redéfinis la méthode en "castant" ton interface, pour récupérer le widget enfant, et appeler ta méthode dessus.

Ce design parait compliqué au 1er abords mais il est puissant, lorsque tu rajouteras un nouveau type de widget, tu n'auras plus à modifier la classe widget.
Tu ne devras la modifier que si tu veux rajouté une méthode dans ta classe Widget que tu veux appliquer à tout tes widgets enfants. (Et je pense que tu pourrait même améliorer ce design avec des pointeurs de fonctions)

Bref..., juste pour te montrer à quel point le c++ est puissant, et, se serait dommage de brider ça puissance avec des shared_ptr.
Personnellement je ne les utilises que lors de la gestion des exceptions dans les bloc try/catch afin que la ressources soit libérée même si une exception est levée.

Ou bien pour les classes non copiable et non dérivée, ils peuvent servir aussi.
Mais pas ailleurs. (Et j'évite de les utiliser le plus possible afin d'éviter de devoir modifier ma classe si, un jour je décide de la rendre copiable ou bien de la faire dériver)

Je pense que SFML utilise pas mal de classe non copiable, Laurent doit sûrement avoir ses raisons car en effet il y a des choses qu'il ne vaudrait pas mieux copié par soucis de performance. (Mais à aucun endroit dans son code je le vois utiliser des shared_ptr et ce genre de chose)
Peut être parce que sa librairie n'utilise pas le c++11 ce qui est dommage mais bon..., en contre partie tu ne dépends pas du shared pointer et tu peux faire dériver ses classes comme tu l'entends et les cloner correctement.

PS : le mot clé auto je ne suis pas très fan non plus et je trouve qu'il sert un peu à rien à part à simplifier l'écriture et éviter de devoir utiliser un typedef dans une méthode. (Mais on pourrait très bien se passé de ce mot clé)

PS 2 : pour les fonctions de calleback j'utilise un delegate pour pouvoir changer la valeur des paramètres de la fonction callback avant son appel, ce qui m'implique de devoir connaître le type de la fonction lorsque je veux changer la valeur de ses paramètres. (Sauf si ce n'est pas une lambda expression ou une fonction anonyme car là le c++ peut déduire le type de la fonction automatiquement lors de la déduction de paramètres template)

Mais en général j'évite d'utiliser les lambda expression qui posent pas mal de problèmes aussi lorsqu'on veut modifier la valeur des paramètres de l'expression.
« Modifié: Août 15, 2014, 11:35:45 am par Lolilolight »

Lolilolight

  • Hero Member
  • *****
  • Messages: 1232
    • Voir le profil
Re : SimpleGui
« Réponse #5 le: Août 15, 2014, 05:00:26 pm »
Il y avait une erreur dans mon algorithme désolé, voici le bon code :

class Widget {
void exampleMethod (BaseInterface& bi) {
    for (unsigned int i = 0; i < children.size(); i++) {
           children[i]->inheritedExampleMethod(*static_cast<Widget&>(bi).children[i]);
    }
}
virtual void inheritedExampleMethod(Widget &w) = 0;
};
 

Et dans la classe bouton :
class Button : public Widget, public Interface<Button> {
    Button() : Interface<Button>(*this) {}
    void exampleMethod(Widget& w) {
            inheritedExampleMethod(static_cast<Button&>(w));
    }
    void inheritedExampleMethod(Button &button) {
             //Définition de la méthode ici.
    }
};
 

Voilà.  :)

Cmdu76

  • Full Member
  • ***
  • Messages: 194
    • Voir le profil
Re : SimpleGui
« Réponse #6 le: Août 16, 2014, 01:21:27 pm »
Tu veux cloner un bouton ? Rien de plus simple :

SGUI::Container container;
auto button1 = container.createButton();
auto button2 = std::make_shared<SGUI::Button>(*button1);
container.addWidget(button2);
 

Il faut juste pas oublier de le rajouter au Container par la suite :)

Mais tu viens de me donner une excellente idée, je vais rajouter un paramètre facultatif à createButton() qui prendra un Button à copier

Les std::shared_ptr sont très bien conçu, il serait dommage de s'en priver...
Leurs avantages sont nombreux :
- Ils "s'autogèrent" pour éviter les erreurs basiques sur les pointeurs
- Ils sont "shared" donc une modification d'un Widget dans le code, entraîne sa modification dans le Container
- Ils restent des pointeurs donc ils permettent le polymorphisme

PS : Les "auto" sont principalement là par feignantise, mais rien ne t'empêche ne pas les utiliser, car je ne les utilise que dans les fonctions d'exemples.

PS 2 : Ensuite les Callback, j'utilise les lambdas et cela fonctionne très bien.
« Modifié: Août 16, 2014, 01:35:31 pm par Cmdu76 »

Lolilolight

  • Hero Member
  • *****
  • Messages: 1232
    • Voir le profil
Re : SimpleGui
« Réponse #7 le: Août 16, 2014, 02:05:45 pm »
Tu arrives à faire des callback avec des lambdas , même avec des fonctions callback qui prennent des arguments et rappeler plusieurs fois la fonction callback avec des arguments différents ?
Bref moi j'ai fais un système qui permet de faire ça, c'est un genre à std::bind à part que tu peux stocker le wrapper sur le pointeur de fonction dans un contenaire et le connecter à un identifiant afin de changer la valeur des arguments avant chaque appel de la fonction callback. (Utile pour passer des informations sur les événements à la fonction de callback)
Mais mon système gère aussi les fonctions anonyme. (Et donc les lambdas)

Je pense même que je vais faire une interface comme en java du style ActionListener en lui passant le bouton qui a été cliqué en paramètre à une fonction que l'on pourra redéfinir.

Pour copier un objet il te faut en réallouer un nom ? (Et pas juste copier l'adresse du bouton)
Ou alors, j'ai loupé quelque chose ???

PS : à moins que tu utilises std::move ???
Dans ce cas là, les std::unique_ptr et les std::shared_ptr me semblent en effet plus appropriés. (D'ailleurs je me demande si je ne vais pas essayer de les utiliser aussi mais je les comprends pas encore assez bien pour ça)


Non j'ai dis une connerie std::move fais juste un transfert de donnée, pas une copie.

J'ai enfin trouvé un bonne article (en Anglais) qui m'a éclairé sur le sujet.
http://www.codeproject.com/Articles/570638/Ten-Cplusplus-Features-Every-Cplusplus-Developer

Je ne savais pas que avec un std::shared on pouvait copier je pensais que ça contenais juste un compteur qui comptait le nombre de fois que le pointeurs était utilisé dans le programme.
« Modifié: Août 16, 2014, 02:17:53 pm par Lolilolight »

Cmdu76

  • Full Member
  • ***
  • Messages: 194
    • Voir le profil
Re : SimpleGui
« Réponse #8 le: Août 16, 2014, 03:37:50 pm »
Ton système a l'air sympa et pratique mais pour SimpleGui je ne veux pas de dépendance (hormis SFML)

Ensuite, si tu regardes le code j'ai posté en haut :

auto button2 = std::make_shared<SGUI::Button>(*button1);

Ici on copie juste les données car button1 est un Button::Ptr (std::shared_ptr<SGUI::Button) donc *button1 est un Button donc on ne prends pas son addresse

PS : Merci pour cet article, je ne connaissais pas les override et les final ! Je trouve que ça va être utile :)
« Modifié: Août 16, 2014, 04:28:19 pm par Cmdu76 »

Lolilolight

  • Hero Member
  • *****
  • Messages: 1232
    • Voir le profil
Re : SimpleGui
« Réponse #9 le: Août 18, 2014, 10:09:54 am »
Ha oké on peut transféré le contenu d'un shared pointer dans un autre, je ne savais pas que l'on pouvait faire ça, comme je te l'ai dis je pensais que un shared pointer ne faisait que compter les occurrences et détruire automatiquement le pointeur lorsqu'il ne pointait plus sur rien.

Bref, ça me sera utile. :)

Les std::unique_ptr c'est donc quand le contenu du pointeur ne peut pas être copié.

Merci pour ses infos.


Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32498
    • Voir le profil
    • SFML's website
    • E-mail
Re : SimpleGui
« Réponse #10 le: Août 18, 2014, 11:16:35 am »
Citer
Ha oké on peut transféré le contenu d'un shared pointer dans un autre, je ne savais pas que l'on pouvait faire ça, comme je te l'ai dis je pensais que un shared pointer ne faisait que compter les occurrences et détruire automatiquement le pointeur lorsqu'il ne pointait plus sur rien.
Un shared_ptr ne fait qu'encapsuler un pointeur vers une instance. Un fois que tu accèdes à cette instance tu peux en faire ce que tu veux, y compris la copier. Là tout ce qu'il a fait c'est allouer un nouveau SGUI::Button en utilisant le constructeur par copie; rien à voir avec le fait qu'il le mette ensuite dans un shared_ptr.

Citer
Les std::unique_ptr c'est donc quand le contenu du pointeur ne peut pas être copié.
Là encore ça n'a rien à voir. Tu peux déréférencer ton unique_ptr et faire une copie de l'objet puis le mettre dans un nouveau unique_ptr si tu veux. La sémantique de unique_ptr c'est de forcer un unique "owner" sur une ressource, par opposition a shared_ptr qui va permettre de partager la ressource à plusieurs owners.
Laurent Gomila - SFML developer

TheKingArthur

  • Invité
Re : SimpleGui
« Réponse #11 le: Août 18, 2014, 11:04:16 pm »
Tout a fait.

En fait j'en savais rien mais sa m'a permis d'apprendre.

Lolilolight

  • Hero Member
  • *****
  • Messages: 1232
    • Voir le profil
Re : SimpleGui
« Réponse #12 le: Août 19, 2014, 06:49:38 pm »
Ok, je comprend mieux maintenant.