Bonjour à tous, depuis longtemps je n'ai pas écrit d'article sur la vie du projet sur le hub
Consulo est un fork d'IntelliJ IDEA Community Edition qui prend en charge .NET (C #), Java
Commençons
Q: API Consulo UI - qu'est-ce que c'est?
R: Il s'agit d'un ensemble d'API pour créer une interface utilisateur. En fait, un simple ensemble d'interfaces qui répète différents composants - Button, RadionButton, Label, etc.
Q: Quel est le but de créer un autre ensemble d'API d'interface utilisateur s'il y avait déjà Swing (puisque IDEA UI utilise Swing pour afficher l'interface)
R: Pour cela, examinons l'idée que j'ai suivie tout en travaillant sur l'API Consulo UI. Étant donné que je suis le principal et presque le seul contributeur au projet Consulo, avec le temps, il m'est devenu difficile de maintenir le nombre de projets qui existent actuellement (environ 156 dépôts). Il y avait une question sur l'analyse de code de masse, mais il est impossible de le faire dans le cadre d'une instance IDE sur la plate-forme Desktop , et je ne voulais pas m'exercer à utiliser des plug-ins de JetBrains où un projet idée-ultime détient tous les plugins pour un certain nombre de raisons.
L'idée est née de l'analyse sur un serveur web. «L'analyse habituelle» ne me convenait pas beaucoup sur le serveur Web, je voulais créer un IDE Web (au moins en lecture seule au début) - tout en ayant toujours les mêmes fonctionnalités que sur le bureau.
Vous pouvez dire que cela répète un peu d' Upsource , l'idée elle-même est similaire - mais l'approche est complètement différente.
Et puis vint le moment - où l'idée était, mais on ne savait pas comment le faire. J'avais de l'expérience avec les frameworks GWT, Vaadin - je ne voulais pas utiliser d'autres frameworks non Java pour générer JS (enfin, ou des js simples).
J'ai pris un mois pour faire des recherches à ce sujet. C'était un test de mes capacités dans cette partie. Au début, je n'utilisais que GWT - pour plus d'informations, j'ai utilisé le RPC intégré.
L'objectif était simple: le projet était déjà ouvert, il suffisait d'afficher l' arborescence du projet + les onglets de l'éditeur . Dans ce cas, tout aurait dû être similaire à la version Desktop.
Immédiatement, il y a eu des problèmes avec le nouveau backend. Par exemple, utiliser EventQueue pour des actions internes
EventQueue pour faire court, c'est un flux d'interface utilisateur (AWT, Swing); presque tout ce qui est lié à l'interface utilisateur s'y produit - rendu, traitement d'un clic de bouton, etc.
Historiquement, dans IDEA, les actions d'écriture doivent toujours être effectuées dans un thread d'interface utilisateur.
L'action d'écriture est un enregistrement dans un fichier ou des modifications apportées à un service (par exemple, renommer un module)
Au début, le problème avec EventQueue pouvait être ignoré - mais ensuite d'autres problèmes sont apparus. Par exemple, des icônes banales. Imaginez que nous ayons un arbre de projet
- [] nom-projet
- [] src
- [] test
- [] build.gradle
Et pour chaque fichier, nous devons télécharger et afficher une image. Puisque nous travaillons dans le code Swing, nous utilisons la classe javax.swing.Icon . Le problème est que c'est juste une interface - qui a tellement d'implémentations différentes
- L'icône d'image est une icône qui enveloppe simplement l'image (c'est-à-dire une image ordinaire du système de fichiers)
- icône layred une icône en couches qui se compose de deux ou plusieurs icônes empilées les unes sur les autres
- icône désactivée - icône avec filtre gris appliqué
- icône transparente - icône avec la transparence spécifiée
- et bien d'autres
Par conséquent, pour afficher l'icône dans le navigateur, vous devez prendre en charge l'ensemble du zoo (et presque tout à la fois). Un des problèmes liés est qu'une icône qui vous est complètement inconnue pour un fichier peut apparaître (par exemple, un symbole est dessiné pixel par pixel à l'intérieur d'un plugin) - et autres doivent être ignorés.
Avec une méthode de béquille (enfin, où sans eux) - une décision a été prise. Il est banal de vérifier via instanceof le type dont nous avons besoin - et d'ignorer tous les autres.
Après un certain temps, la navigation dans le système de fichiers, l'ouverture d'un fichier, la mise en évidence de la syntaxe, l'analyse sémantique, les informations doc rapides, la navigation pour les références de code ont été prises en charge (une combinaison telle que Ctrl + B ou Ctrl + MouseClick1 était prise en charge). En substance, l'éditeur était très similaire à la plate-forme de bureau.
À quoi cela ressemblait:

Donc - rendre l'interface Web était possible avec ma force. Mais c'était un travail très difficile - qui devait être refait. Et puis Vaadin est venu à la rescousse.
J'ai décidé de refaire mon implémentation GWT pour utiliser le framework Vaadin. Ce test s'est avéré très mauvais (les performances ont été très dégradées) - mon expérience de l'utilisation de Vaadin l'a plus affecté et j'ai rejeté cette option (j'ai même fait une réinitialisation matérielle sur le brunch actuel pour l'oublier: D).
Mais l'expérience de l'utilisation de Vaadin m'a toujours été utile, l'idée est née - d'unifier l'interface utilisateur afin que vous puissiez écrire un code, mais obtenir des résultats différents en sortie, selon la plate-forme.
Une autre raison d'unifier l'interface utilisateur est le zoo complet des composants Swing à l'intérieur de la plateforme IntelliJ. Un exemple d'un tel problème est deux implémentations Tabs complètement différentes.


Séparez la logique de l'interface utilisateur:
- frontend - un ensemble d'interfaces pour chaque élément, par exemple consulo.ui.Button # create ()
- backend - implémentation dépendante de la plateforme
- Swing - implémentation de bureau
- WGWT - implémentation web
Qu'est-ce que WGWT ? Acronymes pour Wrapper GWT. Il s'agit d'un framework auto-écrit - qui stockait l'ETAT du composant et l'envoyait via le WebSocket au navigateur (qui à son tour générait du HTML). Il a écrit avec un œil sur Vaadin (oui oui - une autre béquille).
Le temps a passé - et déjà je pouvais lancer une interface utilisateur de test qui fonctionnait de la même manière sur le bureau et dans le navigateur

J'ai également utilisé Vaadin au travail en parallèle, car c'est l'une des options les moins chères pour créer une interface utilisateur Web si vous utilisez Java. J'ai étudié de plus en plus Vaadin - et j'ai décidé de réécrire WGWT à Vaadin à nouveau, mais avec quelques corrections.
Quelles ont été les modifications:
- refus d'utiliser presque tous les composants Vaadin. Il y avait plusieurs raisons - l'une d'entre elles était des composants trop limités (la personnalisation était minime).
- en utilisant les composants existants de mon framework WGWT, c'est-à-dire leur implémentation GWT
- il y avait aussi un correctif qui vous permettait d'écrire des annotations Connect sans lien direct avec le composant serveur (cela a été fait plus pour la structure du projet, afin d'éviter la disponibilité des classes de serveur à l'intérieur du code client)
En conséquence, cela s'est avéré comme ceci:
- frontend - un ensemble d'interfaces pour chaque élément, par exemple consulo.ui.Button # create ()
- backend - implémentation actuelle en fonction de la plateforme
- Swing - implémentation de bureau
- Vaadin - implémentation web
- Android? - pour que le téléphone s'éteigne au début de l'application: D Jusqu'à présent, uniquement au niveau de l'idée qu'il sera possible d'utiliser un code existant pour transférer vers Android (car il n'y aura pas de liaison avec Swing)
Et c'est ainsi que l'API actuelle de Consulo UI est née.
Où l'API Consulo UI sera-t-elle utilisée?
- Dans tous les plugins. AWT / Swing sera "bloqué" (plus de java.awt.Color ) pendant la compilation (un processeur javac sera fait - plus tard, il ne sera peut-être plus nécessaire du tout avec l'arrivée de java 9). Votre ensemble de composants n'est pas une panacée, je comprends cela. Pour le moment, vous pouvez créer votre propre composant d'interface utilisateur personnalisé, jusqu'à présent uniquement du côté Swing (et dans de tels cas, il sera nécessaire d'ajouter une dépendance au plugin consulo.destop afin d'éviter des problèmes sur le serveur Web). Il n'y a pas encore de création de composants Vaadin du côté du plugin - ce sera une tâche mineure.
- Côté plate-forme, il s'agit de Paramètres / Préférences, Exécuter les configurations, Editeur - essentiellement toute l'interface qui va au JFrame.
Quels sont les problèmes?
- Complètement incompatible avec le code AWT / Swing (il existe une classe de béquilles TargetAWT / TargetVaadin qui a des méthodes pour convertir les composants, mais ces classes ne sont pas accessibles pour les plugins).
Tous les composants Swing ne peuvent pas être affichés dans le navigateur - par conséquent, vous devez réécrire tout ce code.
Presque partout, l'API Consulo UI est déjà prise en charge à l'intérieur de la plate-forme - cela vous permet déjà d'utiliser le nouveau framework UI à l'intérieur des plugins et pas seulement. - L'attachement très fort de la plate-forme IntelliJ à Swing, elle est si profondément enfouie que sans la "prochaine" béquille, vous ne pouvez pas la creuser (
)
Après un certain temps
Ce code fonctionne de la même manière sur les deux plates-formes.
Son travail sur Desktop:

Son travail dans le navigateur:

Concernant les problèmes ci-dessus:
- Icônes. La classe consulo.ui.image.Image a été introduite, qui est une image du système de fichiers (et pas seulement). Vous pouvez utiliser la méthode consulo.ui.image.Image # create (java.net.URL) pour télécharger une image.
Sur la plate-forme de bureau - les icônes sont chargées comme elles l'ont été auparavant, le type de retour est seulement maintenant SwingImageRef (le nom de la classe héritée - anciennement consulo.ui.image.Image s'appelait consulo.ui.ImageRef) - une interface qui hérite de javax.swing.Icon et consulo.ui .image.Image. Plus tard, cette interface sera supprimée (son existence est due à une migration simplifiée vers un nouveau type)
Sur la plateforme Web - l'URL est stockée à l'intérieur de l'objet et est l'identifiant à afficher dans l'interface (via URL - / app / uiImage = URLhashCode )
La classe ImageEffects a été introduite. Il possède en lui-même les méthodes nécessaires pour créer des icônes dérivées. Par exemple, #grayed (Image) renverra une icône avec un filtre gris, #transparent (Image) une icône translucide.
C'est-à-dire que tout le zoo décrit ci-dessus a été entraîné dans des cadres étroits.
La prise en charge du rendu manuel des éléments (enfin, sans cela) sera également introduite. La méthode ImageEffects # canvas (int height, int width, Consumer <Canvas2D> painterConsumer) renverra une icône qui sera dessinée via Canvas2D
Sur le bureau - le wrapper sera utilisé en plus de Graphics2D standard de Swing
Sur le Web - chaque appel aux méthodes Canvas2D sera enregistré, puis transféré vers le navigateur où le canevas interne du navigateur sera utilisé
- Écrire l'action dans le thread d'interface utilisateur. Oooo Il n'y a pas encore de solution à ce problème. Pour le moment - il existe un prototype d' action d'écriture dans son propre thread, mais jusqu'à présent uniquement sur la plate-forme Web, trop de choses doivent être modifiées à l'intérieur de la plate-forme pour "être déployées" sur le bureau.
- L'interface utilisateur a été unifiée - pas de zoo pour les éléments simples
Un nouveau problème est également apparu - les boîtes de dialogue Swing bloquent le thread d'exécution pendant le spectacle. En conséquence, IDEA aime écrire du code sous cette forme:
DialogWrapper wrapper = ...; int value = wrapper.showAndGet(); if(value == DialogWrapper.OK) { ... }
Dans le même temps, l'affichage des boîtes de dialogue dans Vaadin ne bloque pas le thread d'exécution.
Pour éviter toute confusion avec l'affichage synchrone et asynchrone des dialogues, une option asynchrone a été choisie (le code ci-dessus devra être repensé et refait).
Résumé
Après un certain temps, j'ai un prototype fonctionnel d'une application Web.

Jusqu'à présent, c'est un prototype qui se dirige vers sa sortie - mais il ne sera pas rapide (hélas).