Olá pessoal, não escrevi um artigo sobre a vida do projeto em Habré há muito tempo. Decidi melhorar e provavelmente começar com o que estou trabalhando agora, a API da interface do usuário do Consulo.
Consulo é uma bifurcação do IntelliJ IDEA Community Edition que oferece suporte para .NET (C #), Java
Vamos começar
P: API da interface do usuário do Consulo - o que é?
R: Este é um conjunto de APIs para criar uma interface do usuário. De fato, um conjunto simples de interfaces que repete diferentes componentes - Button, RadionButton, Label, etc.
P: Qual é o objetivo de criar outro conjunto de APIs da interface do usuário se já houver Swing (já que a IDEA UI usa o Swing para exibir a interface)
R: Para isso, vamos nos aprofundar na ideia que segui enquanto trabalhava na API da interface do usuário do Consulo. Como sou o principal e quase o único colaborador do projeto Consulo, ao longo do tempo tornou-se difícil manter o número de projetos existentes (cerca de 156 repositórios). Havia uma pergunta sobre a análise de código de massa, mas é impossível fazê-lo dentro da estrutura de uma instância IDE na plataforma Desktop , e eu não queria praticar o uso de plug-ins do JetBrains, onde um projeto de idéia final contém todos os plug-ins por vários motivos.
A idéia surgiu da análise em um servidor web. A "análise usual" não me agradou muito no servidor da Web; eu queria criar um IDE da Web (pelo menos apenas de leitura no início) - embora ainda tivesse todas as mesmas funcionalidades que na área de trabalho.
Você pode dizer que isso repete um pouco de Upsource , a ideia em si é semelhante - mas a abordagem é completamente diferente.
E então chegou o momento - quando a idéia era, mas não se sabia como fazê-lo. Tive experiência usando estruturas GWT e Vaadin - não queria usar outras estruturas não Java para gerar JS (js simples ou simples).
Levei um mês para pesquisar isso. Foi um teste das minhas capacidades nesta parte. No começo, usei apenas o GWT - para obter informações, usei o RPC interno.
Havia um objetivo simples - o projeto já estava aberto, era apenas necessário exibir as guias Árvore de Projetos + Editor . Nesse caso, tudo deveria ter sido semelhante à versão para desktop.
Imediatamente houve problemas com o back-end recém-criado. Por exemplo, usando EventQueue para ações internas
EventQueue, para resumir, é um fluxo de interface do usuário (AWT, Swing); quase tudo relacionado à interface do usuário acontece nele - renderização, processamento de um clique no botão e assim por diante.
Historicamente, no IDEA, as ações de gravação sempre devem ser executadas em um thread da interface do usuário.
A ação de gravação é um registro em um arquivo ou altera para algum serviço (por exemplo, renomear um módulo)
No início, o problema com o EventQueue poderia ser ignorado - mas outros problemas apareceram. Por exemplo, ícones banais. Imagine que temos uma árvore de projeto
- [] nome do projeto
- [] src
- [] teste
- [] build.gradle
E para cada arquivo, precisamos enviar e exibir uma imagem. Como trabalhamos dentro do código Swing, usamos a classe javax.swing.Icon . O problema é que é apenas uma interface - com tantas implementações diferentes
- ícone de imagem é um ícone que simplesmente envolve a imagem (ou seja, uma imagem regular do sistema de arquivos)
- ícone de camadas um ícone em camadas que consiste em dois ou mais ícones empilhados uns sobre os outros
- ícone desativado - ícone com filtro cinza aplicado
- ícone transparente - ícone com a transparência especificada
- e muitos outros
Como resultado, para mostrar o ícone no navegador, você precisa oferecer suporte a todo o zoológico (e quase todos de uma vez) .Um dos problemas relacionados é que um ícone completamente desconhecido para você para um arquivo pode aparecer (por exemplo, um símbolo é desenhado pixel por pixel dentro de algum plug-in) - e precisa ser ignorado.
Com um método de muleta (bem, onde sem eles) - uma decisão foi tomada. É banal verificar através de instanceof o tipo de que precisamos - e ignorar todos os outros.
Depois de um tempo, foi fornecido suporte para navegar no sistema de arquivos, abrir um arquivo, destacar sintaxe, análise semântica, informações rápidas sobre documentos, navegação para referências de código (uma combinação como Ctrl + B ou Ctrl + MouseClick1 foi suportada). Em essência, o Editor era muito semelhante à plataforma Desktop.
Como era:

Então - tornar possível a interface da Web com a minha força. Mas foi um trabalho muito difícil - que teve que ser refeito. E então Vaadin veio em socorro.
Decidi refazer minha implementação do GWT para usar a estrutura Vaadin. Esse teste acabou sendo muito ruim (o desempenho foi muito degradado) - minha experiência de usar o Vaadin o afetou mais e eu rejeitei essa opção (até fiz uma redefinição no brunch atual para esquecê-lo: D).
Mas a experiência de usar o Vaadin foi útil para mim o tempo todo, surgiu a idéia - unificar a interface do usuário para que você possa escrever um código, mas obter resultados diferentes na saída, dependendo da plataforma.
Outro motivo para unificar a interface do usuário é o zoológico completo dos componentes Swing dentro da plataforma IntelliJ. Um exemplo desse problema são duas implementações de guias completamente diferentes.


Separe a lógica da interface do usuário:
- frontend - um conjunto de interfaces para cada elemento, por exemplo consulo.ui.Button # create ()
- back-end - implementação dependente da plataforma
- Swing - implementação de desktop
- WGWT - implementação na web
O que é o WGWT ? Acrônimos para Wrapper GWT. Essa é uma estrutura auto-escrita - que armazenava o STATE do componente e o enviava pelo WebSocket ao navegador (que por sua vez gerava html). Ele escreveu de olho em Vaadin (sim, sim - outra muleta).
O tempo passou - e eu já podia iniciar uma interface de teste que funcionava da mesma maneira na área de trabalho e no navegador

Também usei o Vaadin no trabalho em paralelo, pois essa é uma das opções mais baratas para criar uma interface da Web se você usa Java. Estudei Vaadin mais e mais - e decidi reescrever o WGWT para Vaadin novamente, mas com algumas correções.
Quais foram as edições:
- recusa em usar quase todos os componentes Vaadin. Havia vários motivos - um deles era componentes muito limitados (a personalização era mínima).
- usando componentes existentes da minha estrutura WGWT; ou seja, sua implementação GWT
- havia também um patch que permitia escrever anotações do Connect sem um link direto para o componente do servidor (isso foi feito mais para a estrutura do projeto, a fim de evitar a disponibilidade de classes de servidor dentro do código do cliente)
Como resultado, ficou assim:
- frontend - um conjunto de interfaces para cada elemento, por exemplo consulo.ui.Button # create ()
- backend - implementação atual, dependendo da plataforma
- Swing - implementação de desktop
- Vaadin - implementação na web
- Android? - para que o telefone se queime no início do aplicativo: D Até agora, apenas no nível da ideia de que será possível usar um código existente para transferir para o Android (porque não haverá vinculação ao Swing)
E assim nasceu a atual API do Consulo UI.
Onde a API da interface do usuário do Consulo será usada?
- Em todos os plugins. O AWT / Swing será "bloqueado" (não haverá mais java.awt.Color ) no momento da compilação (o processador javac será feito - posteriormente, poderá não ser necessário com a chegada do java 9). Seu conjunto de componentes não é uma panacéia, eu entendo isso. No momento, você pode criar seu próprio componente de interface do usuário personalizado, até agora apenas no lado do Swing (e nesses casos, será necessário adicionar uma dependência ao plug-in consulo.destop para evitar problemas no servidor da web). Ainda não há criação de componentes Vaadin no lado do plug-in - será, esta é uma tarefa menor.
- No lado da plataforma, são Configurações / Preferências, Configurações de Execução, Editor - essencialmente toda a interface que vai para o JFrame.
Quais são os problemas?
- Completamente incompatível com o código AWT / Swing (existe uma classe de muleta TargetAWT / TargetVaadin que possui métodos para converter componentes, mas essas classes não são acessíveis para plug-ins).
Todos os componentes do Swing não podem ser exibidos no navegador - como resultado, você precisa reescrever todo esse código.
Em quase todos os lugares, a API da interface do usuário do Consulo já é suportada dentro da plataforma - isso permite que você já use a nova estrutura da interface do usuário dentro de plugins e não apenas. - O forte apego da plataforma IntelliJ ao Swing, está tão profundamente enterrado que, sem a "próxima" muleta, você não pode desenterrá-la (
)
Depois de algum tempo
Esse código funciona da mesma maneira nas duas plataformas.
Seu trabalho no Desktop:

Seu trabalho no navegador:

Em relação aos problemas acima:
- Ícones A classe consulo.ui.image.Image foi introduzida, que é uma imagem do sistema de arquivos (e não apenas). Você pode usar o método consulo.ui.image.Image # create (java.net.URL) para fazer upload de uma imagem.
Na plataforma Desktop - os ícones são carregados como foram carregados anteriormente, só agora o tipo de retorno é SwingImageRef (nome da classe herdada - anteriormente consulo.ui.image.Image foi chamado consulo.ui.ImageRef) - uma interface que herda javax.swing.Icon e consulo.ui .image.image. Posteriormente, essa interface será removida (sua existência se deve à migração simplificada para um novo tipo)
Na plataforma da Web - a URL é armazenada dentro do objeto e é o identificador para exibição na interface (via URL - / app / uiImage = URLhashCode )
A classe ImageEffects foi introduzida. Ele possui os métodos necessários para criar ícones derivados. Por exemplo, #grayed (Image) retornará um ícone com um filtro cinza, #transparent (Image), um ícone translúcido.
Ou seja, todo o zoológico inteiro descrito acima foi levado a quadros estreitos.
Suporte para renderização manual de elementos (bem, onde sem isso) também será introduzido. O método ImageEffects # canvas (int height, int width, Consumer <Canvas2D> painterConsumer) retornará um ícone que será desenhado através do Canvas2D
No ambiente de trabalho - o wrapper será usado sobre o Graphics2D normal do Swing
Na Web - todas as chamadas para os métodos do Canvas2D serão salvas e depois transferidas para o navegador onde será usada a tela interna do navegador.
- Ação de gravação no thread da interface do usuário. Oooo Ainda não há solução para esse problema. No momento - existe um protótipo de Ação de gravação no encadeamento próprio, mas até agora apenas na plataforma da Web, muito precisa ser alterado dentro da plataforma para "implementá-lo" na área de trabalho.
- A interface do usuário foi unificada - nenhum zoológico para elementos simples
Um novo problema também apareceu - as caixas de diálogo Swing bloqueiam o segmento de execução durante o show. Como resultado, o IDEA gosta de escrever código neste formato:
DialogWrapper wrapper = ...; int value = wrapper.showAndGet(); if(value == DialogWrapper.OK) { ... }
Ao mesmo tempo, a exibição de diálogos no Vaadin não bloqueia o encadeamento de execução.
Para evitar confusão com a exibição síncrona e assíncrona dos diálogos, foi escolhida uma opção assíncrona (o código acima terá que ser repensado e refeito).
Sumário
Depois de um tempo, tenho um protótipo funcional de um aplicativo da Web.

Até agora, este é um protótipo que está se aproximando do lançamento - mas não será rápido (infelizmente).