Consulo UI API from idea to prototype

Hello everyone, for a long time I did not write an article about the life of the project on Habré, I decided to improve and perhaps begin with what I’m working on now, namely the Consulo UI API.


Consulo is a fork of IntelliJ IDEA Community Edition that has support for .NET (C #), Java

Let's start


Q: Consulo UI API - what is it?


A: This is a set of API for creating UI. In fact - a simple set of interfaces that repeats different components - Button, RadionButton, Label, etc.


Q: What is the purpose of creating another set of UI APIs if Swing was already there (since IDEA UI uses Swing to display the interface)


A: To do this, let's delve into the idea that I followed while working on the Consulo UI API. Since I am the main and almost the only contributor to the Consulo project, over time it became difficult for me to support the number of projects that are now (about 156 repositories). There was a question about the mass analysis of the code, but it is impossible to do it within the framework of one IDE instance on the Desktop platform, and using the experience of JetBrains where one project idea-ultimate holds all the plugins I didn’t want to practice for several reasons.


The thought of analyzing the web server was born. "Normal analysis" did not suit me much on the web server, I wanted to make a Web IDE (at least readonly at the start) - while having all the same functionality as on the Desktop.


You can say that this repeats a bit of Upsource , the very idea is similar - but the approach is completely different.


And then came the moment - when the idea was, but it was not known how to do it. Behind him was the experience of using GWT, Vaadin frameworks - other non-Java frameworks for generating JS (or plain js) I did not want to use.


I spent a month on this research . It was a test of my capabilities in this part. At first, I only used GWT — I used the built-in RPC for information.


There was a simple goal - the project was already open, it was only necessary to display Project Tree + Editor Tabs . At the same time, everything should be similar to the Desktop version.


Immediately there were problems with the new backend. For example, using EventQueue for internal actions.


EventQueue if it is short for UI (AWT, Swing) a thread in it almost everything is connected with UI - drawing, handling of pressing a button, and so on.
Historically, in IDEA, write actions should always be performed in a UI stream.
Write action is an entry to a file, or a change to a service (for example, renaming a module)

At first, the problem with EventQueue could be ignored - but then other problems appeared. For example, banal icons . Imagine we have a project tree



And for each file we need to upload and display a picture. As we work inside the Swing code, we use the javax.swing.Icon class. The problem is that this is just an interface - which has a lot of different implementations.



As a result, to show the icon in the browser you need to support the entire zoo (and almost all at once). One of the problems is that a completely unknown icon for a file can come (for example, inside some plug-in some character is drawn pixel-by-pixel) need to ignore.


The crutch method (well, where without them) - the decision was made. It is trite to check through instanceof for the type we need - and ignore all the others.


After a while, support was made for navigating through the file system, opening a file, syntax highlighting, semantic analysis, quick doc info, navigation through code references (a combination such as Ctrl + B, or Ctrl + MouseClick1 was supported). In essence, the Editor was very similar to the Desktop platform.


What it looked like:



So - to make the Web interface was possible by my own. But it was a very rough job - that needed to be redone. And then Vaadin came to the rescue.


I decided to remake my GWT implementation using the Vaadin framework. This test turned out to be very bad (the performance was very degraded) - my experience with Vaadin affected me more, and I rejected this option (I even did a hard-reset on the current brunch to forget about it: D).


But the experience of using Vaadin came in handy for me all the time, the thought arose - to unify the UI so that you could write one code, but at the output get a different result depending on the platform.


Another reason to unify the UI is the complete zoo of Swing components inside the IntelliJ platform. An example of such a problem is two completely different implementations of Tabs.




Separate the UI logic:



What is a WGWT ? Abbreviations from Wrapper GWT. This is a self-written framework - which stored the STATE of the component and sent it via the WebSocket to the browser (which in turn generated html). He wrote with an eye to Vaadin (yes, yes - another crutch).


As time went on - and I could already run a test UI that worked equally well on Desktop and in the browser



I also used Vaadin at work in parallel, as this is one of the cheapest options to build a Web UI if you use Java. I studied Vaadin more and more - and I decided to rewrite WGWT to Vaadin again but with some edits.


What were the edits:



The result was this:



And so was born the current used Consulo UI API.


Where will the Consulo UI API be used?



What are the problems?



Some time later


This code works the same on both platforms.


His work on Desktop:



His work in the browser:



Concerning the above problems:



On the Desktop platform, the icons are loaded as they were loaded earlier, only now the return type is SwingImageRef (the legacy of the class name is formerly consulo.ui.image.Image was called consulo.ui.ImageRef) —an interface that inherits javax.swing.Icon and consulo.ui .image.Image. Later this interface will be removed (its existence is due to simplified migration to a new type)


On the Web platform, the URL is stored inside the object, and is an identifier for displaying in the interface (via the URL - / app / uiImage = URLhashCode )


The ImageEffects class was introduced. He has inside the methods that are needed to create derived icons. For example, #grayed (Image) will return an icon with a gray filter, #transparent (Image) a translucent icon.


That is - all of the above zoo was driven into a narrow frame.


There will also be introduced support for manual drawing of elements (well, where without it). The ImageEffects # canvas method (int height, int width, Consumer <Canvas2D> painterConsumer) will return the icon to be drawn through Canvas2D


On the Desktop, a wrapper will be used over the usual Graphics2D from Swing


On the Web - every call to Canvas2D methods will be saved, and then transferred to the browser where the internal Canvas will be used from the browser



There is also a new problem - Swing dialogs block the executing thread during the show. As a result, IDEA like to write code in this form:


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

In this case, the display of dialogs in Vaadin does not block the executing thread.


In order to avoid confusion with the synchronous and asynchronous display of dialogs - the asynchronous option was chosen (the code described above will have to be rethought and redone).


Total


After a while, I have a working prototype of a Web application.



So far this is a prototype that is moving towards its release - but it will not be fast (alas).

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


All Articles