Dans cet article, je vais essayer de parler du framework SpaceVIL (Space of Visual Items Layout), qui est utilisé pour créer des interfaces graphiques utilisateur sur les plates-formes .Net / .Net Core et JVM.
SpaceVIL est un framework multiplateforme et multilingue, il est basé sur la technologie graphique OpenGL, et la bibliothèque GLFW est responsable de la création de fenêtres. À l'aide de ce cadre, vous pouvez travailler et créer des applications client graphiques dans les systèmes d'exploitation Linux, Mac OS X et Windows. Pour les programmeurs C #, cela est particulièrement vrai pour le moment, étant donné que Microsoft ne portera pas WPF sur d'autres systèmes d'exploitation et Avalonia est le seul analogue possible. Une caractéristique de SpaceVIL dans ce cas particulier est le multilinguisme, c'est-à-dire que le framework pour .Net Core peut être utilisé en conjonction avec les langages de programmation suivants: C #, VisualBasic. Le framework JVM peut être utilisé conjointement avec les langages Java et Scala. Autrement dit, SpaceVIL peut être utilisé avec n'importe laquelle de ces langues et le code résultant sera identique, donc lorsque vous basculez vers une autre langue, vous n'aurez pas à le réapprendre.
SpaceVIL est encore au stade alpha, mais malgré cela, le framework peut être pleinement utilisé en ce moment, car le framework a tout ce dont vous avez besoin pour construire à la fois une interface utilisateur complexe et créer des éléments utilisateur visuels complètement nouveaux. Le but de cet article est précisément de vous en convaincre.
SpaceVIL a été développé à partir de zéro et c'est pourquoi le cadre a ses propres principes qui le distinguent des analogues.
- L'utilisateur de SpaceVIL a un contrôle total sur ce qui se passe.
- Toute application écrite dans SpaceVIL sera exactement la même sur toutes les plateformes. Il n'y a pas d'embûches. Vous pouvez utiliser n'importe quelle version de SpaceVIL (.Net / JVM, Mac OS X, Linux, Windows), le résultat et l'apparence seront toujours les mêmes.
- La version SpaceVIL pour JVM est identique dans l'utilisation de la version SpaceVIL pour .Net
- SpaceVIL offre des possibilités de personnalisation approfondie d'un élément, car tous les objets interactifs sont des conteneurs pour d'autres objets interactifs.
- Le cadre est très flexible et facile à utiliser, car il contient peu de règles strictes de base, et la seule chose que vous devez comprendre avant de commencer à travailler est ce que signifient les paramètres de remplissage, de marge, d'alignement (et toute personne qui a créé des interfaces simples les connaît , dans WPF, Android Studio ou écrit des styles en CSS).
- SpaceVIL ne vous demandera pas de faire une étude approfondie de son intérieur et il fera exactement ce que vous écrivez. Tous les éléments obéissent aux règles générales, une approche fonctionnera sur tous les éléments. Après avoir rappelé la base, il sera possible de prédire à la fois la composition des éléments et les façons de la styliser.
- Le cadre est très léger, moins d'un mégaoctet et tout en un seul fichier.
Les possibilités
Voyons maintenant de quoi le framework de la version actuelle est capable.
- 54 éléments sont disponibles, dont 10 conteneurs spécialisés, 6 primitives (éléments non interactifs) et 38 éléments interactifs à des fins diverses.
- Sur la base de tous ces éléments, couplé à la mise en œuvre d'interfaces spéciales, vous pouvez créer vos propres éléments de toute complexité.
- Il y a un style d'éléments, il y a des options pour créer des thèmes de style entiers ou changer / remplacer le style de n'importe quel élément standard dans le cadre. Jusqu'à présent, un seul thème est présent dans SpaceVIL et il est installé par défaut.
- Il y a un système d'états. Chaque élément peut se voir attribuer un état visuel pour l'une des méthodes d'influence externe: survoler l'élément, appuyer sur le bouton de la souris, relâcher le bouton de la souris, basculer, mettre au point et éteindre l'élément.
- Il existe un filtrage des événements. Chaque élément de l'interaction peut filtrer les événements qui le traversent, ce qui permet à un événement de passer à travers l'élément et d'être rejeté par l'autre. Dans un exemple, je vais en parler plus en détail.
- Implémentation d'un système d'éléments indépendants flottants.
- Un système de boîtes de dialogue et d'éléments de dialogue a été mis en place.
- Rendu indépendant implémenté. Chaque fenêtre a deux threads - l'un contrôle le rendu, l'autre exécute des tâches à partir des événements entrants, c'est-à-dire que la fenêtre continue désormais toujours le rendu (et ne se "bloque"), quelle que soit la tâche qui a été lancée après avoir appuyé sur un bouton.
La structure
Voyons maintenant la structure des éléments. Les types d'éléments suivants sont présents dans le cadre: fenêtres, actives et dialogues, conteneurs, pour un positionnement pratique des éléments, éléments interactifs et primitives. Passons brièvement en revue tout ce qui précède. J'espère qu'il n'est pas nécessaire d'expliquer de telles fenêtres, je note seulement que la boîte de dialogue bloque la fenêtre qui l'a provoquée jusqu'à sa fermeture.
Conteneurs
Les types de conteneurs suivants sont présentés dans SpaceVIL:
- Un conteneur commun (les éléments à l'intérieur d'un tel conteneur sont positionnés en raison de paramètres d'alignement, de remplissage, de marge, de taille et de taille).
- Piles verticales et horizontales (les éléments ajoutés à un tel conteneur seront disposés dans l'ordre sans avoir à affiner les paramètres qui sont nécessaires lors de l'utilisation du type de conteneur précédent).
- Grille - Les éléments sont ajoutés aux cellules de la grille et positionnés à l'intérieur de leur cellule.
- List (ListBox, TreeView), un conteneur basé sur une pile verticale, mais avec la possibilité de faire défiler pour afficher les éléments qui ne rentrent pas dans le conteneur.
- Séparateur (SplitArea), un conteneur peut être de deux types - vertical et horizontal, sépare deux zones et contrôle interactivement la taille de ces zones.
- Conteneur à onglets (TabView), contrôle la visibilité des pages.
- WrapGrid, positionne les éléments à l'intérieur des cellules d'une certaine taille, remplit tout l'espace libre en fonction de l'orientation avec possibilité de défilement (l'exemple le plus frappant est l'Explorateur Windows en mode d'affichage des icônes).
- Et enfin, un conteneur gratuit, probablement le conteneur le plus rare de ceux-ci, est une zone infinie sur laquelle vous pouvez ajouter des éléments de taille fixe, mais il est préférable de l'utiliser en combinaison avec un élément de type ResizableItem.
Éléments interactifs
Les éléments de ce type acceptent de nombreux états et ont divers événements. Si c'est plus simple, tout ce avec quoi vous pouvez interagir et il y a des éléments interactifs, par exemple: un bouton, une case à cocher, un élément pour saisir du texte, etc.
Primitifs
Contrairement aux éléments interactifs, il existe des primitifs, des éléments totalement non interactifs, il est impossible de les contacter, ils existent simplement pour se montrer. Types de primitives: un triangle, un rectangle, une ellipse et un peu plus compliqué - une figure arbitraire qui peut prendre la forme de n'importe quelle complexité.
Il existe également des classes de service statiques. Certaines de ces classes permettent au programmeur d'obtenir et de personnaliser des éléments à leur guise. Par exemple, il existe une classe contrôlant les styles d'éléments et une classe avec laquelle vous pouvez définir une forme d'affichage arbitraire pour un élément interactif, une classe de paramètres par défaut, etc.
Un exemple d'application simple utilisant le framework SpaceVIL
J'ai un peu de fantaisie sur les exemples. Vous pouvez toujours trouver un exemple fonctionnel abstrait et reflétant avec précision, mais si vous le lisez de côté, il semble toujours très peu convaincant, donc à titre d'exemple, j'essaie de créer des applications petites mais prêtes à l'emploi qui servent une sorte de but intelligible.
Passons à la description de l'application. Le programme est un éditeur de cartes de héros pour des jeux comme "Dungeons and Dragons" et porte le nom de CharacterEditor. Le programme génère aléatoirement un nombre spécifié de caractères différents avec des noms, âge, race, sexe, classe et caractéristiques. L'utilisateur a la possibilité d'écrire une biographie et de donner au personnage des compétences spécialisées. En conséquence, vous pouvez enregistrer la carte héros en tant que fichier texte. Passons à l'analyse de code. Le programme est écrit en C #. Lorsque vous utilisez Java, le code sera essentiellement le même.
En conséquence, nous obtenons une telle application:

Créer une fenêtre d'application
À ce stade, nous allons créer une fenêtre. Permettez-moi de vous rappeler que SpaceVIL utilise GLFW, donc si vous écrivez une application pour la plate-forme .Net, la bibliothèque GLFW compilée doit être copiée à côté du fichier exécutable. La machine virtuelle Java utilise le wrapper de bibliothèque GLFW (LWJGL), qui a déjà un GLFW compilé dans sa composition.
Ensuite, remplissez la zone de rendu avec les éléments nécessaires et donnez-leur une belle apparence. Les principales étapes pour y parvenir sont les suivantes:
- Initialisation de SpaceVIL avant de l'utiliser. Dans la fonction Main, écrivez simplement:
if (!SpaceVIL.Common.CommonService.InitSpaceVILComponents()) return;
- Créez maintenant une fenêtre. Pour ce faire, vous devez écrire une classe window, en l'héritant de la classe
SpaceVIL.ActiveWindow
, décrire la méthode InitWindow()
et la définir avec plusieurs paramètres de base, tels que le nom de la fenêtre, le texte de la barre de titre et les dimensions. En conséquence, nous obtenons un code qui ressemble à ceci:
using System; using SpaceVIL; namespace CharacterEditor { internal class MainWindow : ActiveWindow { public override void InitWindow() { SetParameters("CharacterEditor", "CharacterEditor", 1000, 600); } } }
- Il ne reste plus qu'à créer une instance de cette classe et à l'appeler. Pour ce faire, nous complétons la méthode Main avec les lignes de code suivantes:
MainWindow mw = new MainWindow(); mw.Show();
C'est tout, à ce stade, vous pouvez démarrer l'application et vérifier si tout fonctionne.
Remplissage d'éléments
Pour implémenter CharacterEditor, j'ai décidé de mettre une barre de titre, une barre d'outils et un séparateur vertical sur la fenêtre. La barre d'outils contiendra: un bouton pour rafraîchir la liste des nouveaux caractères générés, un bouton pour sauvegarder le caractère et un élément avec le nombre de caractères générés. Sur le côté gauche du séparateur vertical se trouve une liste de caractères générés, et sur la droite se trouve une zone de texte pour éditer un caractère sélectionné dans la liste. Afin de ne pas encombrer la classe de fenêtre avec les paramètres des éléments, vous pouvez écrire une classe statique qui nous fournira des éléments prêts en apparence et en paramètres. Lors de l'ajout, il est important de se rappeler que chaque élément interactif, qu'il s'agisse d'un bouton ou d'une liste, est un conteneur, c'est-à-dire que vous pouvez mettre n'importe quoi d'un bouton à n'importe quoi, des primitives à un autre conteneur, et tout élément complexe n'est qu'un ensemble d'éléments plus simples qui, au total, servent un seul objectif. Sachant cela, vous devez vous rappeler la première règle stricte - avant d'ajouter d'autres éléments à un élément, vous devez l'ajouter vous-même quelque part, soit à la classe window elle-même (dans notre cas, c'est MainWindow
), soit à un conteneur ou tout autre élément interactif. Expliquons avec un exemple:
public override void InitWindow() { SetParameters("CharacterEditor", "CharacterEditor", 1000, 600);
Il est juste d'ajouter d'abord un frame
à la fenêtre, puis d'ajouter un bouton au frame
. Il peut sembler que la règle est très gênante et vous devrez transpirer lors de la création d'une fenêtre complexe, c'est pourquoi SpaceVIL vous encourage à créer vos propres éléments, ce qui simplifie considérablement l'ajout d'éléments. Je montrerai et expliquerai la création de mes propres éléments un peu plus tard. Revenons à l'application. Voici la fenêtre résultante:

Analysons maintenant le code:
Code récapitulatif du balisage MainWindow internal ListBox ItemList = new ListBox();
Dans la classe ItemFactory
j'ai décrit l'apparence et la disposition des éléments. Par exemple, la méthode ItemFactory.GetToolbarButton()
ressemble à ceci:
internal static ButtonCore GetToolbarButton() { ButtonCore btn = new ButtonCore();
Les autres éléments sont décrits de la même manière.
Création et application de styles
Comme vous l'avez peut-être remarqué, j'ai appliqué un style à l'élément ItemText
. Voyons donc comment créer des styles et, tout d'abord, jetons un œil au code de style:
Style style = Style.GetTextAreaStyle();
Comme vous pouvez le voir, j'ai pris un style prêt à l' SpaceVIL.Style
classe SpaceVIL.Style
pour cet élément et l' SpaceVIL.Style
légèrement changé, en corrigeant les couleurs. Chaque style peut contenir plusieurs styles internes pour styliser chaque élément complexe constituant. Par exemple, un élément CheckBox
compose d'un conteneur, d'un indicateur et d'un texte, de sorte que son style possède des styles internes pour l'indicateur ("indicateur") et le texte ("ligne de texte").
La classe Style
couvre toutes les propriétés visuelles des éléments et en plus de cela, en utilisant le style, vous pouvez changer de manière interactive la forme d'un élément, par exemple, d'une ellipse à un rectangle et vice versa. Pour appliquer un style, vous devez appeler la méthode SetStyle(Style style)
sur l'élément, comme déjà indiqué ci-dessus:
ItemText.SetStyle(StyleFactory.GetTextAreaStyle());
Créez votre propre article
Passons maintenant à la création de l'élément. L'élément lui-même ne doit pas nécessairement être quelque chose de spécifique, il peut s'agir d'une pile régulière dans laquelle vous ajoutez plusieurs autres éléments. Par exemple, dans l'exemple ci-dessus, j'ai une barre d'outils dans laquelle il y a trois éléments. La barre d'outils elle-même n'est qu'une pile horizontale. Tout pourrait être organisé comme un élément distinct et appelé ToolBar. En soi, cela ne fait rien, mais dans la classe MainWindow
le nombre de lignes serait réduit et la compréhension du balisage serait encore plus facile, de plus, c'est aussi un moyen d'affaiblir la première règle stricte, bien que, bien sûr, au final, tout lui obéisse. D'accord, nous ne touchons plus la barre d'outils. Nous avons besoin d'un élément pour la liste qui affichera le caractère généré.
Pour le rendre plus intéressant, nous définissons la composition de l'élément plus compliqué:
- Une icône de personnage dont la couleur indique l'appartenance à une race fantastique.
- Nom, prénom et race du personnage sous forme de texte.
- Bouton d'aide rapide du personnage (nécessaire pour démontrer les événements de filtrage).
- Bouton pour supprimer le personnage, s'il ne nous convient pas.
Pour créer une classe de votre propre élément, vous devez l'hériter de tout élément interactif de SpaceVIL, si au moins une classe dans sa composition nous convient, mais pour l'exemple actuel, nous assemblerons l'élément à partir de zéro, donc nous l'hériterons de la classe abstraite de base des éléments interactifs - SpaceVIL.Prototype
. Nous devons également implémenter la méthode InitElements()
, dans laquelle nous décrivons l'apparence de l'élément, l'emplacement et le type des éléments imbriqués, ainsi que l'ordre d'ajout des éléments imbriqués. L'élément lui-même sera appelé CharacterCard
.
Passons à l'analyse du code de l'élément fini:
Code d'article de la carte de caractères using System; using System.Drawing; using SpaceVIL; using SpaceVIL.Core; using SpaceVIL.Decorations; using SpaceVIL.Common; namespace CharacterEditor {
Dans l'exemple, j'ai utilisé une classe auxiliaire, qui contient toutes les caractéristiques de base du personnage telles que le nom, le prénom, la race, le sexe, l'âge, la classe de spécialisation, les caractéristiques, les capacités et la biographie. Dans cette classe, tous les paramètres, à l'exception des compétences et de la biographie (il est supposé que l'utilisateur les inventera indépendamment), sont générés.
Traitement et filtrage des événements
Dans l'exemple précédent, deux types d'événements ont été décrits: MouseHover et MouseClick. Il n'y a que 11 événements de base pour le moment, voici la liste:
- EventMouseHover
- EventMouseLeave
- EventMouseClick
- EventMouseDoubleClick
- EventMousePress
- EventMouseDrag
- EventScrollUp
- EventScrollDown
- EventKeyPress
- EventKeyRelease
- EventTextInput
Les éléments complexes ont leurs propres événements uniques, mais les événements ci-dessus sont disponibles (sur réservation) pour tout le monde.
La syntaxe de la gestion des événements est triviale et ressemble à ceci:
Passons maintenant au filtrage des événements. Par défaut, les événements passent par une pyramide d'éléments. Dans notre exemple, le bouton infoBtn
reçoit l'événement de cliquer sur le bouton du bouton infoBtn
, puis cet événement recevra l'élément CharacterCard
, puis le ListBox
dans lequel il sera situé, puis SplitArea
, VerticalStack
et à la fin il atteindra l'élément de base Wontainer
.
Sur chaque élément, vous pouvez gérer l'événement EventMouseClick
et toutes ces actions dans l'ordre spécifié seront effectuées, mais qu'en est-il si en cliquant sur un élément, nous ne voulons pas que cet événement aille plus loin dans la chaîne? Pour cela, il n'y a que le filtrage des événements. Voyons plus clairement, par exemple, sur l'élément CharacterCard
. Imaginez que CharacterCard
décrit l'événement EventMouseClick
, qui insère des informations du CharacterInfo
lié dans un champ de texte pour modifier un caractère. Ce comportement sera logique - on clique sur l'élément et on voit tous les paramètres du personnage. Ensuite, nous éditons le personnage, inventons une biographie et des compétences ou changeons les caractéristiques. À un moment donné, nous voulions voir une brève information sur un autre personnage généré dans la liste et cliquer sur le bouton infoBtn. Si nous ne filtrons pas les événements, après avoir appelé l'info-bulle, EventMouseClick sera exécuté sur l'élément CharacterCard
lui-même, qui, comme nous nous en souvenons, insère du texte dans le champ d'édition de caractères, ce qui entraînera une perte de modifications si nous n'enregistrons pas les résultats, et le comportement de l'application elle-même sera paraître illogique. Par conséquent, afin que l'événement soit exécuté uniquement sur le bouton, nous pouvons définir le filtre à l'aide de la infoBtn.SetPassEvents(false)
.
Si vous appelez cette méthode de cette façon, le bouton cessera d'ignorer les événements après lui-même. Supposons que nous ne voulons pas ignorer les événements de clic de souris uniquement, nous pourrions alors appeler une méthode avec d'autres paramètres, par exemple, infoBtn.SetPassEvents(false, InputEventType.MousePress, MouseRelease)
.
Ainsi, il est possible de filtrer les événements à chaque étape, en obtenant le résultat souhaité.
Vous pouvez à nouveau regarder l'application, qui s'est finalement avérée. Bien sûr, les détails de la mise en œuvre de la logique métier sont omis ici, en particulier, la génération de personnages, leurs compétences et bien plus encore, qui n'est plus directement liée à SpaceVIL. Le code d'application complet peut être consulté sur le lien vers GitHub, où il existe déjà plusieurs autres exemples de travail avec SpaceVIL, à la fois en C # et en Java.
Capture d'écran de l'application CharacterEditor terminée Conclusion
En conclusion, je voudrais vous rappeler que le cadre est en cours de développement actif, de sorte que les plantages et les plantages sont possibles, ainsi que certaines fonctionnalités peuvent être révisées et le résultat final de l'utilisation peut radicalement différer de l'actuel, donc, si la version actuelle du cadre vous convient, alors n'oubliez pas de sauvegarde de cette version, car je ne peux pas garantir que les nouvelles versions seront rétrocompatibles. Certains points peuvent être refaits pour augmenter le confort et la vitesse d'utilisation de SpaceVIL et jusqu'à présent ne veulent pas faire glisser des idées anciennes et abandonnées. De plus, le travail de SpaceVIL n'a pas été testé sur les cartes vidéo d'AMD, en raison du manque d'équipements appropriés. Des tests ont été effectués sur des cartes vidéo d'Intel et de NVidia. Le développement ultérieur de SpaceVIL se concentrera sur l'ajout de nouvelles fonctionnalités (par exemple, il n'y a actuellement aucun support pour les dégradés) et l'optimisation.
Je voudrais également mentionner une limitation qui mérite d'être rappelée lors de l'écriture d'une application multiplateforme utilisant cette technologie - il n'est pas recommandé d'utiliser des boîtes de dialogue (et généralement de créer des applications multi-fenêtres sous Linux en raison d'erreurs de rendu), les boîtes de dialogue peuvent être facilement remplacées par des éléments de dialogue. Mac OS X interdit généralement la création d'applications multi-fenêtres, car il nécessite que l'interface graphique soit lancée uniquement dans le thread d'application principal.
Vous pouvez télécharger le cadre de la version requise et tous les exemples présentés de programmes de test aux liens suivants. La première version de la documentation est également disponible ici.
Enfin, une démonstration un peu plus visuelle. Vous trouverez ci-dessous des applications écrites à l'aide de la technologie SpaceVIL, qui peuvent vous donner une idée de ce que vous pouvez réaliser en utilisant SpaceVIL.
Captures d'écran d'applications écrites avec SpaceVIL Les références