Consulo UI API de la idea al prototipo

Hola a todos, durante mucho tiempo no escribí un artículo sobre la vida del proyecto en el centro


Consulo es una bifurcación de IntelliJ IDEA Community Edition que tiene soporte para .NET (C #), Java

Empecemos


P: API de Consulo UI: ¿qué es?


R: Este es un conjunto de API para crear una interfaz de usuario. De hecho, un conjunto simple de interfaces que repite diferentes componentes: Button, RadionButton, Label, etc.


P: ¿Cuál es el propósito de crear otro conjunto de API de UI si ya existía Swing (ya que IDEA UI usa Swing para mostrar la interfaz)


R: Para esto, profundicemos en la idea que seguí mientras trabajaba en la API de Consulo UI. Como soy el principal y casi el único contribuyente al proyecto Consulo, con el tiempo se me hizo difícil mantener la cantidad de proyectos que hay ahora (alrededor de 156 repositorios). Hubo una pregunta sobre el análisis de código masivo, pero es imposible hacerlo dentro del marco de una instancia IDE en la plataforma Desktop , y no quería practicar el uso de complementos de JetBrains donde un proyecto idea-ultimate contiene todos los complementos por varias razones.


La idea surgió del análisis en un servidor web. El "análisis habitual" no me convenía mucho en el servidor web, quería hacer un IDE web (al menos de solo lectura al inicio), sin dejar de tener la misma funcionalidad que en el escritorio.


Puede decir que esto repite un poco de Upsource , la idea en sí es similar, pero el enfoque es completamente diferente.


Y luego llegó el momento, cuando la idea era, pero no se sabía cómo hacerlo. Tenía experiencia en el uso de frameworks GWT, Vaadin: no quería utilizar otros frameworks que no sean Java para generar JS (bueno, o js simples).


Me tomé un mes para investigar esto. Fue una prueba de mis capacidades en esta parte. Al principio usé solo GWT; para obtener información, usé el RPC incorporado.


Había un objetivo simple: el proyecto ya estaba abierto, solo era necesario mostrar las pestañas del árbol del proyecto + editor . En este caso, todo debería haber sido similar a la versión de escritorio.


Inmediatamente hubo problemas con el backend recién hecho. Por ejemplo, usando EventQueue para acciones internas


EventQueue para abreviar, es una transmisión de interfaz de usuario (AWT, Swing); casi todo lo relacionado con la interfaz de usuario sucede en ella: renderización, procesamiento de un clic en el botón, etc.
Históricamente, en IDEA, las acciones de escritura siempre deben realizarse en un subproceso de interfaz de usuario.
La acción de escritura es un registro en un archivo o cambios en algún servicio (por ejemplo, cambiar el nombre de un módulo)

Al principio, el problema con EventQueue podría ignorarse, pero luego aparecieron otros problemas. Por ejemplo, iconos banales. Imagina que tenemos un árbol de proyectos


  • [] nombre-proyecto
    • [] src
      • [] Main.java
    • [] prueba
    • [] build.gradle

Y para cada archivo necesitamos cargar y mostrar una imagen. Como trabajamos dentro del código Swing, usamos la clase javax.swing.Icon . El problema es que es solo una interfaz, que tiene muchas implementaciones diferentes


  • El icono de imagen es un icono que simplemente envuelve Imagen (es decir, una imagen normal del sistema de archivos)
  • icono en capas un icono en capas que consta de dos o más iconos apilados uno encima del otro
  • icono deshabilitado: icono con filtro gris aplicado
  • icono transparente: icono con la transparencia especificada
  • y muchos otros

Como resultado, para mostrar el ícono en el navegador, debe admitir todo el zoológico (y casi todo a la vez). Uno de los problemas que lo acompañan es que puede aparecer un ícono completamente desconocido para usted para un archivo (por ejemplo, un símbolo se dibuja píxel por píxel dentro de algún complemento), y tal necesita ser ignorado


Con un método de muleta (bueno, sin ellos), se tomó una decisión. Es muy sencillo verificar a través de instanceof el tipo que necesitamos e ignorar a todos los demás.


Después de un tiempo, se admitió la navegación por el sistema de archivos, la apertura de un archivo, el resaltado de sintaxis, el análisis semántico, la información rápida del documento, la navegación por referencias de código (se admite una combinación como Ctrl + B o Ctrl + MouseClick1). En esencia, el editor era muy similar a la plataforma de escritorio.


Lo que parecía:



Entonces, hacer la interfaz web fue posible con mi fuerza. Pero fue un trabajo muy duro, que tuvo que rehacerse. Y luego Vaadin vino al rescate.


Decidí rehacer mi implementación de GWT para usar el marco Vaadin. Esta prueba resultó ser muy mala (el rendimiento fue muy degradado): mi experiencia con el uso de Vaadin la afectó más y rechacé esta opción (incluso hice un restablecimiento completo en el brunch actual para olvidarlo: D).


Pero la experiencia de usar Vaadin fue útil para mí todo el tiempo, surgió la idea: unificar la interfaz de usuario para que pueda escribir un código, pero obtener resultados diferentes en la salida, dependiendo de la plataforma.


Otra razón para unificar la interfaz de usuario es el zoológico completo de componentes Swing dentro de la plataforma IntelliJ. Un ejemplo de tal problema son dos implementaciones de pestañas completamente diferentes.




Separe la lógica de la interfaz de usuario:


  • frontend: un conjunto de interfaces para cada elemento, por ejemplo consulo.ui.Button # create ()
  • backend: implementación dependiente de la plataforma
    • Swing - implementación de escritorio
    • WGWT - implementación web

¿Qué es WGWT ? Acrónimos de Wrapper GWT. Este es un marco autoescrito, que almacena el ESTADO del componente y lo envía a través del WebSocket al navegador (que a su vez generó html). Escribió con un ojo en Vaadin (sí, sí, otra muleta).


Pasó el tiempo, y ya pude iniciar una interfaz de usuario de prueba que funcionó igual en el escritorio y en el navegador



También utilicé Vaadin en el trabajo en paralelo, ya que esta es una de las opciones más baratas para crear una interfaz de usuario web si usa Java. Estudié a Vaadin más y más, y decidí reescribir WGWT a Vaadin nuevamente, pero con algunas correcciones.


¿Cuáles fueron las ediciones?


  • negativa a usar casi todos los componentes de Vaadin. Hubo varias razones: una de ellas era componentes demasiado limitados (la personalización era mínima).
  • usando componentes existentes de mi marco WGWT; es decir, su implementación GWT
  • también había un parche que le permitía escribir anotaciones de Connect sin un enlace directo al componente del servidor (esto se hizo más para la estructura del proyecto, a fin de evitar la disponibilidad de clases de servidor dentro del código del cliente)

Como resultado, resultó así:


  • frontend: un conjunto de interfaces para cada elemento, por ejemplo consulo.ui.Button # create ()
  • backend: implementación actual según la plataforma
    • Swing - implementación de escritorio
    • Vaadin - implementación web
    • Android? - para que el teléfono se queme al inicio de la aplicación: D Hasta ahora, solo a nivel de la idea de que será posible usar un código existente para transferir a Android (porque no habrá enlace para Swing)

Y así nació la actual API de Consulo UI.


¿Dónde se utilizará la API de Consulo UI?


  • En todos los complementos. AWT / Swing se "bloqueará" (no más java.awt.Color ) durante la compilación (se realizará un procesador javac; más tarde, puede que no sea necesario en absoluto con la llegada de java 9). Su conjunto de componentes no es una panacea, eso lo entiendo. Por el momento, puede crear su propio componente de interfaz de usuario personalizado, hasta ahora solo en el lado Swing (y en tales casos será necesario agregar una dependencia al complemento consulo.destop para evitar problemas en el servidor web). Todavía no hay creación de componentes Vaadin en el lado del complemento, será una tarea menor.
  • En el lado de la plataforma, estos son Configuración / Preferencias, Ejecutar configuraciones, Editor, esencialmente toda la interfaz que va al JFrame.

Cuales son los problemas?


  • Completamente incompatible con el código AWT / Swing (hay una clase de muleta TargetAWT / TargetVaadin que tiene métodos para convertir componentes, pero estas clases no son accesibles para los complementos).
    Todos los componentes de Swing no se pueden mostrar en el navegador; como resultado, debe volver a escribir todo este código.
    Casi en todas partes, la API de Consulo UI ya es compatible dentro de la plataforma; esto le permite usar el nuevo marco de interfaz de usuario dentro de los complementos y no solo.
  • El apego muy fuerte de la plataforma IntelliJ a Swing, está enterrado tan profundamente que sin la "siguiente" muleta, no se puede desenterrar ( )

Después de un tiempo


Este código funciona igual en ambas plataformas.


Su trabajo en el escritorio:



Su trabajo en el navegador:



Con respecto a los problemas anteriores:


  • Iconos Se introdujo la clase consulo.ui.image.Image, que es una imagen del sistema de archivos (y no solo). Puede usar el método consulo.ui.image.Image # create (java.net.URL) para cargar una imagen.

En la plataforma de escritorio: los iconos se cargan como se cargaron antes, solo que ahora el tipo de retorno es SwingImageRef (el nombre de clase heredado, anteriormente consulo.ui.image.Image se llamaba consulo.ui.ImageRef), una interfaz que hereda javax.swing.Icon y consulo.ui .imagen.imagen. Más tarde, esta interfaz se eliminará (su existencia se debe a la migración simplificada a un nuevo tipo)


En la plataforma web: la URL se almacena dentro del objeto y es el identificador para mostrar en la interfaz (a través de URL - / app / uiImage = URLhashCode )


Se ha introducido la clase ImageEffects. Tiene dentro de sí mismo los métodos necesarios para crear iconos derivados. Por ejemplo, #grayed (Image) devolverá un ícono con un filtro gris, #transparent (Image) un ícono translúcido.


Es decir, todo el zoológico descrito anteriormente fue conducido a marcos estrechos.


También se introducirá soporte para la representación manual de elementos (bueno, sin esto). Método ImageEffects # canvas (int height, int width, Consumer <Canvas2D> painterConsumer) devolverá un icono que se dibujará a través de Canvas2D


En el escritorio: el contenedor se usará encima de Graphics2D normal de Swing


En la Web: todas las llamadas a los métodos Canvas2D se guardarán y luego se transferirán al navegador donde se utilizará el Canvas interno del navegador


  • Escribir acción en subproceso de interfaz de usuario. Oooo No hay solución a este problema todavía. Por el momento, hay un prototipo de Acción de escritura en Own Thread, pero hasta ahora solo en la plataforma web, es necesario cambiar demasiado dentro de la plataforma para "desplegarlo" en el escritorio.
  • La interfaz de usuario se ha unificado: no hay zoológico para elementos simples

También apareció un nuevo problema: los cuadros de diálogo Swing bloquean el hilo de ejecución durante el espectáculo. Como resultado, IDEA le gusta escribir código de esta forma:


DialogWrapper wrapper = ...; int value = wrapper.showAndGet(); if(value == DialogWrapper.OK) { ... } 

Al mismo tiempo, mostrar cuadros de diálogo en Vaadin no bloquea el hilo de ejecución.


Para evitar confusiones con la visualización síncrona y asíncrona de los cuadros de diálogo, se eligió una opción asíncrona (el código anterior deberá ser repensado y rehecho).


Resumen


Después de un tiempo, tengo un prototipo funcional de una aplicación web.



Hasta ahora, este es un prototipo que se está moviendo hacia su lanzamiento, pero no será rápido (por desgracia).

Source: https://habr.com/ru/post/es414497/


All Articles