|
Publicité ' | ||||||||||||||||||||||||
|
|
#61 | |
![]() ![]() |
Citation:
Les widgets (ou quel que soit le terme utilisé) sont bel et bien le noeud du problème, mais votre discussion ne semble pas prête à faire ressortir une idée utilisable. Comme je l'ai dit, que l'on utilise la souris ou le doigt (ou pourquoi pas l'oeil, au travers d'une caméra qui calcule l'endroit où l'on regarde) comme mécanisme de pointage, le fait que ce soit quelque chose de tout récent ou, au contraire d'antédéluvien n'a que peu d'importance
__________________
en bas de page
|
|
|
|
00
|
|
|
#62 |
![]() ![]() yan verdavaineIngénieur expert Inscription : mars 2004 Messages : 9 870 ![]() |
@kaola.
tu devrais bien relire. Car au finale on ne cherche pas a se prouver quelque chose et on as parlé de deux architectures. Pour proposer une solution, il faut déjà avoir un l'objectif. Que propose tu? 1- se baser sur l'architecture "widget" comme Qt, wxWidget, ... 2- quelque chose plus dans l'aire du temps? 3- les deux ? 4- un concept différent? En gros, qu'es ce tu propose comme liberté au développeur pour la création de son IHM? Si tu veut prendre en compte les erreurs/solutions du passé. Il faut les exposer. A partir de là, on devrais trouver un objectif concret. De plus, on est pas obligé de se baser sur la GDI de l'os pour garder le look. Par exemple Qt l'utilise pour dessiner les primitive de base au travers un style. Mais c'est Qt qui dessine toute l'ihm. Qu'es qui empêcherai de faire pareil? |
|
|
00
|
|
|
#63 | |||||
![]() ![]() |
Citation:
Citation:
Citation:
Si c'est possible (comprenez: il faut vraiment que l'on y réfléchisse lors de l'étape de conception Par la suite il serait donc possible d'envisager (pour ceux qui le souhaitent ou qui en ont l'usage) la mise au point de moteurs de rendus différents (3D, OpenGl, SVG...) Citation:
Mais si cela peut, effectivement déboucher sur des aspects concrets, je vous demanderai juste d'éviter le coté "querelle de clochers" Citation:
Je me dis que le premier moteur de rendu serait sans doute basé sur les primitives de l'os, mais que nous pourrions effectivement évoluer vers d'autres moteurs Mais, c'est bien l'occasion de se rappeler du conseil de Jean Marc (repris par d'autres, d'ailleurs De ce point de vue, je proposerais d'admettre l'idée d'une façade et de moteurs de rendu comme acquise, mais de se limiter (du moins, dans un premier temps) au seul moteur de rendu utilisant les primitives OS, et de classer les autres possibilités déjà évoquées sur le sujet dans la catégorie des "évolutions possibles"
__________________
en bas de page
|
|||||
|
|
00
|
|
|
#64 |
|
Membre Expert
![]() Inscription : avril 2009 Messages : 1 359 ![]() |
Est il nécessaire pour avoir un système de Widget (c'est à dire des briques de base pour la construction de l'IHM, honnêtement, je vois mal comment on peut s'en passer...), d'avoir un superObjet?
Il me semble qu'on peut justement envisager les éléments d'interface comme des agrégats, peut être implémentés comme des policies ou des traits (?), qui géreraient : - l'accès aux données (un ensemble d'itérateurs, pointant vers les données?) - la réception de messages (des dispositifs de pointage, de saisie, ou des autres éléments, quelque chose comme un triplet : emetteur, condition, message) - l'envoi de message (paires messages destination?) - le rendu (sous forme d'une liste d'instructions de base) Je ne suis pas certain de l'intérêt que cela présente par rapport à une classe abstraite, mais ca me parait assez faisable... Une question qui se pose, alors, est: a-t-on besoin de définir, comme dans les frameworks usuels, une hiérarchie d'éléments? Personnellement je crois qu'il en faut au moins 2 : une permettant de gérer la mémoire (qui détient quoi, qui libère quoi), une autre relative à l'affichage (pour batir le zOrder passé au moteur de rendu) Francois |
|
|
00
|
|
|
#65 | ||||
![]() ![]() |
Citation:
Citation:
Citation:
La décision d'ajouter, par exemple, un moteur de rendu pourrait se limiter strictement à l'ajout de quelques traits politiques et alias de types, au lieu de devoir recréer toute une hiérarchie En écrivant une fois du code, on peut donc très facilement avoir... un grand nombre de possibilités Citation:
Mais, là encore, C++ nous donne les moyens d'éviter d'avoir à recopier du code sans cesse
__________________
en bas de page
|
||||
|
|
00
|
|
|
#66 | |
|
Expert Confirmé
![]() ![]() Joel LamotteDéveloppeur de jeux vidéo Inscription : août 2004 Messages : 1 554 ![]() |
Citation:
En fait en relisant, j'étais en train de me dire qu'il faudrait même une 3ème partie qui correspondrait à la gestion des entrées, l'abstraction souris/clavier/autre. En la faisant aussi séparément, on gagne la même flexibilitée qu'avec la séparation de la partie rendu : on peut avoir du code qui s'adapte facilement à différents types d'entrées ou de manières de récupérer les informations des périfériques. A priori la partie rendu et la partie entrées seraient souvent liées (par exemple si on fournit les deux exploitant l'API de l'OS) mais pourraient être remplacées séparément au besoin sans que l'essentiel du code manipulant l'IHM soit impacté (ou peu impacté). (mais je dis peut être une bêtise, a force de tout séparer il peut peut être y avoir des problèmes non-triviaux non-évidents) |
|
|
00
|
|
|
#67 | |
![]() ![]() |
Citation:
Allez, je te charrie Parce que je trouve ta remarque tout à fait pertinente A tel point que j'accepte la proposition, sous réserve toujours de faisabilité au moment de la conception (car, pour l'instant, on ne fait que du brainstorming, hein
__________________
en bas de page
|
|
|
|
00
|
|
|
#68 | |
|
Membre Expert
![]() Inscription : avril 2009 Messages : 1 359 ![]() |
Citation:
En gros, tu vas avoir une fenetre, qui contient un panel, qui contient une grille, qui contient des check box, etc... Tous les éléments sont rangés dans un arbre. Si l'on a besoin d'un objet application, l'application elle même est un "superarbre" qui contient toutes les fenetres, avec leurs petits arbres... Mais il n'y a pas d'hierarchie au sens de l'héritage, juste des liens parents/enfants... Et là, je crois qu'il faut au moins deux hiérarchies : une pour la "possession" des objets, une autre pour le rendu... Maintenant, en lisant klaim, je me dis qu'il en faut probablement une troisième pour les entrées, si l'on admet que les messages "remontent" le long de l'arbre (ce n'est pas une obligation, mais c'est souvent malin...) Dans la description de l'interface, un élément aurait alors 3 parents : - son créateur : chargé de le libérer en fin de course - son parent de rendu : sur lequel il est dessiné (avec ou sans clipping) - son parent de pointage : vers lequel remontent ses messages Normalement les trois sont confondus, mais en les séparant, je crois qu'on peut faire des tas de choses amusantes... (exemples : parent de rendu différent du créateur : on peut afficher les parties d'un élément en plusieurs endroits, par exemple, une barre de commande en haut, et un tableau en bas... parent de pointage différent du créateur on peut faire qu'un élément d'un bloc, eg barre de commande, opère sur un composant particulier, qui n'appartient pas à cette barre...) Francois |
|
|
|
00
|
|
|
#69 |
|
Expert Confirmé
![]() ![]() Joel LamotteDéveloppeur de jeux vidéo Inscription : août 2004 Messages : 1 554 ![]() |
koala01> Ne me lance pas des fleurs, je risque de prendre confiance ^^;;;
Plus sérieusement, je pense que ces idées me sont venues aussi par rapport a ce que j'ai appris jusqu'ici sur le développement de mon jeu perso. Il s'agit d'un jeu de stratégie où l'un des point principaux est la possibilité de "pirater" l'interface de l'autre (via le déclenchement de "virus"), soit en lui faisant afficher des informations fausses, soit en la "perturbant"(de manière plus ou moins génante). C'est aussi valide sur le comportement de la souris et du clavier, les entrées (par exemple j'ai un "virus" qui inverse les mouvements de la souris...). Travailler sur ce jeu m'a obligé a séparer conceptuellement tout ça, avoir des couches additionnelles d'abstraction pour permettre un piratage "propre" d'interface. C'est vrai que souvent on pense l'interface utilisateur comme un sensemble lié et juste séparé du code métier. Mais a force je suis bien obligé de le voir comme un ensemble de parties séparées qui pourraient être modifiées indépendemment. Mais sans plus d'expérience dans le développement d'IHM, je me méfie de mes conclusions dans le cas présent puisqu'il se pourrait que cela complexifie inutilement les choses. Ou au contraire, ça pourrait simplifier le monde. Difficile a dire d'ici. Ca m'interesse de tester voir en tout cas. |
|
00
|
|
|
#70 | |||||||
![]() ![]() |
Citation:
Nous dirons donc qu'il y a, effectivement, trois aspects (pour éviter de parler de hiérarchie et les risques de confusion de terme que cela engendre) à prendre en ligne de compte, et nous sommes donc bien d'accord Mais, bien que je me rende compte que le pattern composite est incontournable, il me semble encore une fois important de préciser que l'agrégation et les template devraient être préférés à l'héritage, pour éviter que, une grille étant un élément de l'arborescence que l'on trouve dans une fenêtre, on n'en vienne à estimer qu'il y a de quoi avoir une classe commune entre la fenêtre et la grille (par exemple Citation:
Citation:
Citation:
Citation:
Citation:
Mais, s'il y a bien un fait qui est, me semble-t-il, valable partout, c'est que, plus tu arrive à "découpler" les différents aspects, plus tu t'offre de possibilités d'évolution. Et ca se traduit généralement par un desing plus simple. Citation:
Si la conception est (comment savoir si elle est plus simple... je n'ai pas d'étalon pour comparer
__________________
en bas de page
|
|||||||
|
|
00
|
|
|
#71 |
|
Membre chevronné
![]() Inscription : juin 2009 Messages : 632 ![]() |
Je vous conseillerai de commencer à travailler avec le système de windowing de l'OS plutôt qu'un système complètement owner drawn.
Des trucs qui me passent par la tête: L'abstraction pour les objets physiques et les primitives devrait naturellement être opérée de manière statique avec, d'après moi, des traits classes. Une interface unifiée pour manipuler des handles, points, rects, icons, menus, frames, controls... Une implémentation par plateforme ciblée avec potentiellement des implementations vraiment customs qui opéreraient sur du très bas niveau (GDI/GDK...) et donc fourniraient du rendu custom. Les events de win32, gtk (et d'autres) sont identifiés par des ID numériques. On a donc la possibilité de specialiser des functions/classes templates pour le traitement de ces events. Quelque chose de plus dynamique pourrait être étudié avec des libs comme Boost.Signal2 mais il me parait important de pouvoir spécifier des events de manières "statique" car la plupart du temps, une application travaille à partir d'un nombre fini d'events pre-déterminés. Avec les variadic templates, il doit y avoir moyen de faire des choses sympatiques. Le fait qu'un widget "écoute" ou non un event pourrait être spécifié sous forme de policy. L'utilisateur pourrait composer ses propres policies ou utiliser des widgets standards fournis par la lib. Le pattern composite me semble aussi indispensable pour s'assurer d'une bonne restitution des ressources. Passer par des shared pointers de widgets? |
|
00
|
|
|
#72 |
|
Membre Expert
![]() Inscription : avril 2009 Messages : 1 359 ![]() |
2 commentaires...
Sur le pattern composite, il n'est pas forcément nécessaire... Les éléments d'interface forment "plus ou moins" un arbre, mais on n'a pas un vrai composite. En fait, il y a quelques éléments conteneurs (fenêtres, panels), et pas mal d'éléments unitaires (boutons, checkbox, etc...). Quant au dessin, ou la remontée des messages, le composite est une solution, mais c'est loin d'être la seule. En fait, je crois que la seule bonne raison de faire du composite, c'est que les OS implémentent l'interface plus ou moins comme cela... Mais il faut garder en tête que cela pose autant de problèmes que cela en règle. Il me parait logique qu'il y ait un composite quelque part, en bas de l"implémentation (pour refléter l'OS), mais moins certain que ce soit la structure qu'il faut présenter à l'utilisateur... Sur les messages, je me demandais ce matin jusqu'à quel point ils sont nécessaire. Comme les composites, il en faut à bas niveau parce que c'est comme ca que les OS fonctionnent. Mais la méthode de "messages statiques" proposée par metagoto ne revient elle pas à éliminer la notion de message du framework client? Francois |
|
|
00
|
|
|
#73 |
![]() ![]() |
Bon, comme promis, voici un petit résumé de la manière dont j'envisagerais le projet.
Il est divisé en six parties:
Il est possible d'affiner ou de demander des précisions sur quatre points particuliers, mais ceux-ci ne seront en aucun cas remis en cause
Parmi les idées dont il faut évaluer la faisabilité, nous trouvons celle qui consiste à séparer très clairement trois aspect généralement intimement liés que sont:
S'il est effectivement envisageable de séparer les différents aspects sans que cela ne finisse en "casse-tête chinois" à la conception, il s'agit réellement d'une idée qui vaut la peine d'être mise en oeuvre. Une autre partie importante à évaluer réside dans l'utilisation de boost, que l'on peut scinder en deux aspects distincts:
Libre à vous de commenter ces propositions, tant sur l'utilité réelle que sur la stabilité de la partie envisagée, ou d'en proposer d'autres (il va de soi que l'utilisation de any, tribool, et autres parties dédiées aux conteneurs, par exemple, est d'office admise comme utiles en cas de besoin et "au cas par cas"
La décision d'utiliser boost et les possibilités de c++11 place un nombre assez important de restrictions, notamment en terme de compatibilité avec le matériel et / ou les compilateurs anciens (pour lesquels aucun support de C++11 n'est envisageable). Nous pourrions décider de laisser, du moins dans un premier temps, délibérément les anciens compilateurs sur le coté, mais est-ce raisonnable Si nous les laissons sur le coté pour l'instant, n'aurons nous pas énormément de mal à assurer la compatibilité par la suite Si nous les prenons dés le départ en compte, ne risque-t-on pas de se rendre la tâche impossible, compte tenu des décisions déjà prises Enfin, l'aspect purement utile de la bibliothèque ne serait-il pas remis en cause si nous venions à décider de ne prendre que les "derniers" compilateurs en compte A titre personnel, je rejoins l'avis de Jean Marc, qui est de se concentrer d'abord sur les nouveaux compilateurs, mais je reste ouvert à la discussion, pour autant que cela ne vienne pas complexifier outre mesure un projet qui sera déjà très ambitieux sans cela... N'hésitez pas à essayer de me convaincre que la complexification ne sera pas si importante que cela 4- Les choix d'intégration Une IHM qui ne pourrait interagir avec aucun format ou protocole existant et clairement formalisé n'aurait aucun intérêt. La question est donc de savoir la priorité que l'on porte à l'intégration de chaque format / type de donnée / protocole, certains devant être intégrés dés le départ (sans doute parce qu'utilisés "en interne", d'autre pouvant être intégrés par la suite. Il me semblerait en effet dangereux voir utopiste de vouloir tenter de tout intégré au début: il faut garder l'idée de base "aussi simple que possible". Dés lors, que faut il intégrer "directement", qu'est-ce qui "peut attendre" et qu'est-ce qui est simplement inutile (ou en tout cas pas primordial) dans la liste suivante, qui n'a rien d'exhaustif
Prenez cependant en compte le fait que la bibliothèque veut rester non intrusive... Ce n'est donc pas forcément parce que le support d'un format ou d'un protocole n'est pas directement intégré dans la bibliothèque qu'il est, fatalement, inutilisable. Au pire, il ne sera utilisable qu'à condition... d'être présent par ailleurs sur la machine de développement de l'utilisateur 5- Les évolutions prévisibles: Il est entendu que le projet de base doit rester le plus simple possible, car il présentera déjà un nombre important de défis à relever. L'idée est donc, au début, d'arriver à fournir quelque chose de "basique" (comprenez: limité dans le choix des intégrations ou des moteurs de rendu disponibles, par exemple), mais de stable à l'utilisation (qui ne provoque pas de fuites mémoire en tous les sens, par exemple Une fois le noyau de base stabilisé, nous pourrons envisager de rajouter des fonctionnalités diverses et variées. Parmi celles-ci, on compte :
J'ai beau être ouvert à toute discussion, il faut malheureusement parfois trancher, autrement on n'avance plus. J'ai donc écarté définitivement un certain nombre de propositions, parce qu'elles entraient en conflit avec des décisions déjà prises. L'une d'entre elle était de considérer la bibliothèque comme une "surcouche" de l'existant. La raison est que je ne veux pas simplement "refaire" ce qui existe, mais que je souhaite réellement partir d'une page blanche, afin d'adopter une approche totalement différente et innovante, sans pour autant oublier les enseignements engrangés par l'existant. Le fait de se baser sur l'existant aurait beaucoup trop tendance à... nous inciter à reproduire ce qui a déjà été fait. Voilà, je crois avoir fait un des résumés les plus longs de l'histoire du forum Maintenant, n'hésitez pas à réagir sur les différents points [EDIT] Une dernière précision Le projet est déjà en lui-même ambitieux, il ne faut pas se le cacher. Certains d'entre vous pourraient avoir peur de "ne pas être assez qualifié" pour y participer, d'autres pourraient simplement manquer de temps pour le faire, et d'autres enfin seront dans les deux situations. Mon sentiment est que toute intervention, toute suggestion est bonne à prendre, et que c'est justement l'avis de ceux qui "ont du mal" avec l'existant qui permettra de faire quelque chose d'utile à tous. Une question, une suggestion, ca n'a l'air de rien, mais, si cela permet d'ouvrir une porte vers un point de vue original, cela n'a pas de prix. De plus, vous aurez sans doute remarqué à la lecture de ce "résumé" qu'il y a de nombreux aspects à envisager. Peut-être n'êtes vous pas forcément qualifié dans chacun d'eux, mais peut être avez vous une expérience significative dans un domaine particulier bien précis. C'est cette expérience significative qui m'intéresse à la limite bien plus que votre "non qualification" dans le reste. Il est tout à fait possible de vous faire évoluer dans ce que vous ne maitrisez pas, mais, par contre, si on ne profite pas de votre maitrise particulière, qui pourrait nous l'apporter et nous faire évoluer nous
__________________
en bas de page
|
|
|
00
|
|
|
#74 |
|
Membre Expert
![]() Inscription : avril 2009 Messages : 1 359 ![]() |
Sur le résumé, je n'ai pas grand chose à dire. C'est un peu trop "implémentation" à mon gout, mais cela reflète ce qui s'est dit.
J'ai quelques commentaires/questions sur le fond, qui sont, à mon avis, des conséquences immédiates de l'abandon du SuperObjet (la principale décision prise si j'en crois le résumé précédent) 1- les callbacks : une raison bêtement pratique au superObjet, c'est qu'une IHM utilise beaucoup de callbacks. Le superObjet, en faisant descendre tous les élément du framework d'une même classe, rend "compatibles" leurs pointeurs sur fonctions membres. Je sais qu'il existe des façons de tourner cette difficulté, mais je pense qu'il faut en discuter assez vite. Les callbacks, c'est au coeur de l'IHM... 2- Le pattern composite. La structure composite de l'IHM est grandement facilitée par l'existence d'un type 'superWidget'. La présence d'une classe parente commune est la façon usuelle d'implémenter le pattern composite. Si l'on abandonne le superObjet, je suppose que ce n'est pas pour introduire le superWidget. Doit on alors abandonner le pattern composite? ou l'implémenter différemment? 3- Le langage cible. Pour qu'un framework soit utilisé, il faut qu'il soit simple d'emploi. Dans le cas d'un framework d'IHM, l'utilisation, et surtout l'extension de celui ci, impose que l'utilisateur comprenne la façon dont les éléments sont construits. Les frameworks à SuperObjet, qui s'appuient sur une hiérarchie de classes, c'est de la POO très classique, le genre de chose que n'importe quel développeur comprend. C'est également assez simple à appliquer à l'interface (puisque les "objets" dans ce cas, sont aussi concrets que des boutons, des listbox, et que leurs méthodes sont exactement çà...). Les approches que nous comptons suivre (traits, templates variadiques et autres) ne sont pas tout à fait grand public (et ne le deviendront pas dans les 5 années qui viennent, ne rêvons pas). Comment faire que le framework soit néanmoins accessible au plus grand nombre? (j'observe que la plupart des spécialistes présents sur le forum déclarent ne pas trop aimer l'IHM, et donc ne sont pas dans la cible...) Entendons nous bien, je ne cherche nullement à remettre en cause l'idée de n'avoir pas de superObjet, mais je pense qu'il faut attaquer de front les problèmes qui en découlent. Francois |
|
|
00
|
|
|
#75 |
|
Membre Expert
![]() ![]() Inscription : juillet 2008 Messages : 1 580 ![]() |
Concernant le dernier point. C'est l'avantage des bibliothéques, ça cache les détails d'implémentation. Un exemple simple : boost::function, c'est d'une simplicité ultime à l'utilisation et pourtant en interne, c'est pas à la porté de tout le monde de comprendre ce qu'il se passe. (notamment à cause de l'emploi massif de propocessor metaprogramming) Et ça gêne personne.
Et quand j'ai déclaré ne pas aimé l'IHM, j'aime pas l'utiliser car je trouve ça long et ennuyeux (y'a vraiment pas de challenge mental à mon gout) mais c'est pas pour ça que je n'utilise pas Qt (ah et soyons clair, je réagit sur le point de la déclaration du "pas aimé l'ihm", pas au "spécialiste" :') )
__________________
"Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu |
|
|
00
|
|
|
#76 | |
|
Membre Expert
![]() Inscription : avril 2009 Messages : 1 359 ![]() |
Citation:
1- tu interdis toute extension, sauf à aller dans le code difficile (je ne donne pas cher du framework) 2- tu le permets, au travers d'un langage d'extension simple, mais alors il faut gérer ce langage 3- tu essayes d'avoir un code de départ suffisament simple (pour les parties extensibles, le moteur de rendu, ou les détails de la pompe à message, ce n'est pas nécessaire) pour les parties extensibles Il me semble que le point 3 s'impose. Ce que j'essaie de dire, je crois, c'est qu'il faut que les parties "extensibles" du framework (la définition de widgets, la customisation du rendu, l'ajout de messages et de comportements) soit suffisament simple pour être abordable par le developpeur d'interface type, qui n'est généralement pas un pro des nouveautés du langage... Francois |
|
|
|
00
|
|
|
#77 | |||||
![]() ![]() |
Citation:
Mon avis est que le super objet, pour facile qu'il soit, incite rapidement à ne pas réfléchir aux autres solutions, qui pourraient pourtant s'avérer au moins aussi intéressantes. L'abandon du super objet permet donc, fatalement, de s'ouvrir de nouveaux horizons, ce qui est bel et bien le but de "l'exercice". De plus, j'ai déjà largement exprimé (dans la discussion qui traite du sujet) ma désapprobation du principe de super objet du point de vue de la conception Citation:
Citation:
C'est donc à nous de déterminer exactement quel contenu va dans quel contenant dans notre conception Citation:
Citation:
Chaque décision prise apporte son lot de contrainte ou de possibilités, et, si nous voulons arriver à quelque chose de bien, il est très important d'aller au fond des choses
__________________
en bas de page
|
|||||
|
|
00
|
|
|
#78 | |||
![]() ![]() |
Citation:
Citation:
Bien sur, il y a des gens qui préfèrent de loin la ligne de commande, mais dans certaines situations, il faut avouer que le fait d'avoir une IHM rend les choses plus "user friendly" Citation:
__________________
en bas de page
|
|||
|
|
00
|
|
|
#79 | |||
|
Expert Confirmé Sénior
![]() ![]() ![]() Inscription : novembre 2005 Messages : 4 970 ![]() |
Citation:
Citation:
Citation:
__________________
Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça. |
|||
|
|
00
|
|
|
#80 | |
|
Membre Expert
![]() Inscription : avril 2009 Messages : 1 359 ![]() |
Je pense qu'il y a confusion entre deux choses...
- Le framework proprement dit, c'est à dire la façon dont on envisage de représenter et de gérer l'interface utilisateur, et les éléments qui la composent. - La librairie d'élements d'interface, c'est à dire que ce que le framework fournit en "standard" à l'utilisateur de base, pour qu'il fabrique son interface en trois clics. Les accesseurs Bdd, ou les parseurs XML, appartiennent à la seconde catégorie. Citation:
Le seul point d'internationalisation qui relève spécifiquement de l'IHM, c'est la direction d'écriture des langues, parce qu'elle a un effet sur la façon dont les composants d'interface s'alignent. Francois |
|
|
|
00
|
Copyright © 2000-2013 - www.developpez.com