Back to Home

IntelliJ IDEA plugin development. Part 7

intellij idea · plugins · development

IntelliJ IDEA plugin development. Part 7

Original author: JetBrains
  • Transfer
In this part: user interface components. The previous part is here .


IntelliJ IDEA includes a large number of custom Swing components. Using these components in your plugins ensures that they look and work in harmony with the rest of the IDE's user interface and often allow you to reduce code size compared to using standard Swing components.

Menus and Toolbars


Menus and toolbars (toolbars) are built using a system of actions (as already described in the second part).

Tool windows


Tool windows are panels that appear to the left, bottom, and right of the main IntelliJ IDEA window. On each side there are two groups of tool windows - primary and secondary, and at the same time only one group can be active.

Each tool window can contain several tabs (called “contents” in the API). For example, the toolwindow “Run” shows a tab for each active run configuration, and “Changes” displays a fixed set of tabs depending on the version control system used in the project.

There are two main scenarios for using tool windows in plugins. In the first scenario (used, for example, in Ant and Commander plug-ins), the tool window button is always displayed, therefore, the user can activate and interact with the plug-in functionality at any time. In the second scenario (used by the "Analyze Dependencies" action), a tool window is created to show the results of a specific operation and can be closed by the user immediately after the operation is completed.

In the first scenario, the window is registered in the plugin.xml file using the extension point
<>
settings. Extension point attributes contain data that is used to display the toolwindow button:
  • Attribute “id” (required) - corresponds to the text displayed on the toolwindow button;
  • Attribute "anchor" (required) - sets the side of the screen to which the tool window is attached ("left", "right" or "bottom");
  • Boolean attribute “secondary” (optional) - indicates whether the tool window will belong to the secondary group;
  • “Icon” (optional) - the icon that will be displayed on the button (13 x 13 pixels).

In addition to this, you should specify the factory class (in the attribute "factoryClass") that implements the ToolWindowFactory interface . When the user clicks on the tool window button, the createToolWindowContent () method is called and initializes the user interface. This procedure ensures that unused tool windows do not cause any overhead in memory usage or startup time: if the user does not interact with the toolwindow of your plug-in, then no code will be downloaded or executed.

If the tool window is not required for all types of projects, you can specify the optional attribute conditionClass containing the fully qualified name of the class that implements the Condition interface (this may be the same class that implements the factory). If the value () method returns false, the tool window will not be displayed. Note that the condition is calculated only once when loading the project. If you want to show and hide tool windows dynamically while working with the project, you need to use the second script for registering the tool window.

Configuration example

The second scenario consists in the usual call to the ToolWindowManager .registerToolWindow () method from the plugin code. This method has several overloads that can be used depending on your tasks. If you use an overload that accepts a Swing component, then it becomes the first tab displayed in the tool window.

Displaying the contents of many toolwindows requires access to indexes. By virtue of this, windows are turned off when building indexes, but it will remain active if you pass “true” as the value of the canWorkInDumbMode parameter to the registerToolWindow () function.

As mentioned earlier, tool windows can contain multiple tabs. To manage the contents of a window, you can call ToolWindow.getContentManager (). To add a tab (“content”), you must first create it by calling ContentManager .getFactory (). CreateContent (), and then add it to the tool window using ContentManager.addContent ().

You can determine whether the user is allowed to close tabs, either globally or individually. The latter is done by defining the canCloseContents parameter of the registerToolWindow () function, or by specifying
in the plugin.xml file. Even if closing tabs is enabled globally, you can disable it for a specific tab by calling
.

Dialogues


DialogWrapper - is the base class for all modal (and some non-modal) dialog boxes used in the IntelliJ IDEA plugin. It provides the following features:
  • Button layout (platform-specific order of OK / Cancel buttons, Mac OS-specific Help button);
  • Context help;
  • Remembering the size of the dialog box;
  • Validation of data (and displaying an error message if the data entered in the dialog box is incorrect);
  • Keyboard shortcuts: Esc to close the dialog box, left / right to switch between the buttons and Y / N to answer “Yes / No” if they are present in the dialog box;
  • Optional "Do not ask again" checkbox.

When using the DialogWrapper class for your own dialog box, you must do the following:
  • Call the constructor of the base class and pass the project within which the dialog box or the parent component for the dialog box will be displayed;
  • Call the init () method from the dialog box class constructor;
  • Call the setTitle () method to set the title for the dialog box;
  • Implement the createCenterPanel () method to return the component responsible for the main content of the dialog box;
  • Optional: Override the getPreferredFocusedComponent () method, which returns the component that will be in focus when the dialog box appears;
  • Optional: Override the getDimensionServiceKey () method to determine the identifier that will be used to save the size of the dialog box;
  • Optional: Override the getHelpId () method to set the help context associated with the dialog box.

The DialogWrapper class is often used in conjunction with UI Designer forms. In order to bind the form and your class that extends DialogWrapper, bind the root panel of the form to the field and return it from the createCenterPanel () method.

To display the dialog box, call the show () method and then use the getExitCode () method to check how the dialog box was closed.

To configure the buttons displayed in the dialog box (i.e., replace the standard set of OK / Cancel / Help buttons), you can override the createActions () or createLeftActions () methods. Both of these methods return an array of Swing Action objects. If the button you add closes the dialog box, you can use DialogWrapperExitAction as the base class for the action.

To verify the data entered in the dialog box, you can override the doValidate () method. The method will be called automatically by timer. If the currently entered data is valid, you need to return null. Otherwise, return a ValidationInfo object that encapsulates the error message and, optionally, a component associated with invalid data. If you specify a component, an error icon will be displayed next to it and it will receive focus when the user tries to click OK.

Popup windows


The IntelliJ IDEA user interface makes extensive use of pop-ups - semi-modal windows that do not have an explicit close button and automatically disappear when focus is lost. Using these controls in your plugin provides a single user interface between the plugin and the rest of the integrated development environment.

Pop-ups can have a title, if necessary they can be moved and allow resizing (remembering the size is supported), they can be nested (it is allowed to show additional pop-ups when an item is selected). JBPopupFactory

Interfaceallows you to create pop-ups displaying various kinds of Swing components, depending on your specific needs. The most commonly used methods are:
  • createComponentPopupBuilder () is the most universal way to show any Swing component inside a popup.
  • createListPopupBuilder () - creates a popup for selecting one or more items from a Swing JList.
  • createConfirmation () - creates a pop-up window for choosing between two options and performing various actions depending on the selected one.
  • createActionGroupPopup () - creates a popup that shows a group of actions and performs the one selected by the user.

Pop-ups for an action group support a variety of ways to select an action using the keyboard, in addition to the usual arrow keys. By passing the value of one of the constants to the ActionSelectionAid enumeration, you can choose whether it will be possible to select an action by pressing the numeric key corresponding to its serial number, entering part of its text (accelerated search), or pressing the mnemonic symbol. For pop-ups with a fixed set of elements, it is recommended to choose the method of serial numbering; for pop-ups with a variable and potentially large number of elements - an accelerated search usually works best.

If you need to create a list popup dialog, but a normal JList does not suit you - for example, it is undesirable to group actions into a group, you can work directly with the ListPopupStep interface and the JBPopupFactory.createListPopup () method. Usually you do not need to implement the entire interface; instead, you can inherit from the BaseListPopupStep class . The main methods for overriding are: getTextFor () (returns the text displayed for the element) and onChosen () (called when the element is selected). By returning a new PopupStep from the onChosen () method, hierarchical (nested) pop-ups can be implemented.

Once you have created the window, you need to display it by calling one of the show () methods. You can let IntelliJ IDEA automatically select a position based on context by calling showInBestPositionFor (), or explicitly specify a position through methods such as showUnderneathOf () and showInCenterOf (). Note that the show () methods immediately return control, rather than waiting for the popup to close. If you need to perform some actions when closing a window, you can bind a listener to it using the addListener () method; or override a method like PopupStep.onChosen (); or attach an event handler to the corresponding component inside the popup.

Notifications


One of the guiding design principles in recent versions of IntelliJ IDEA is to prevent the use of modal windows to notify the user of errors and other situations that may require his attention. As a replacement, IntelliJ IDEA provides a choice of several modeless notification types.

Dialogues

When working with a modal dialog box, instead of checking the entered data by clicking on the OK button and notifying the user of invalid data in another modal window, it is recommended to use the DialogBuilder.doValidate () method, which was described earlier.

Editor Tips

For actions called from the editor (for example, refactoring, navigation, etc.), the best way to notify the user about the impossibility of performing actions is to use the HintManager class . Its showErrorHint () method displays a window floating above the editor that hides automatically when the user starts another action in the editor. Other HintManager methods can be used to display other types of modeless notifications in the form of hints above the editor.

High level notifications

The most common way to display modeless notifications is to use the Notifications class . It has two main advantages:
  • the user can manage each type of notifications listed in the Settings | Notifications ”;
  • all displayed notifications are collected in the Event Log tool window and can be viewed later.

The main method used to display a notification is Notifications.Bus.notify (). Notification text may contain HTML tags. You can allow the user to interact with the notification by including hyperlinks and passing an instance of NotificationListener to the constructor of the Notification class .

The GroupDisplayId parameter of the Notication constructor indicates the type of notification; the user can select the type of display corresponding to the type of each notification in the Settings | Notifications. To indicate your preferred type, you must call Notifications.Bus.register () before displaying the notification.

Class and file selection


File selection

To allow the user to select a file, directory, or multiple files, use the FileChooser .chooseFiles () method . This method has several overloads, one of which returns void and receives a callback function that takes a list of selected files as a parameter. This is the only overload that will display the native dialog box on Mac OS X. FileChooserDescriptor

classAllows you to control which files can be selected. Designer options determine whether files and / or directories can be selected, and whether multiple items are allowed. For more precise control, you can overload the isFileSelectable () method. You can also customize the display of files by overriding the getIcon (), getName (), and getComment () methods of the FileChooserDescriptor class. Note that the native Mac OS X file selection dialog does not support most of the settings, so if you rely on them, you must use the chooseFiles () method overload, which displays the standard IntelliJ IDEA dialog box.

A very common way to select a file is to use a text box to enter a path with an ellipsis ("...") to display a file selection dialog. To create such a control, use the TextFieldWithBrowseButton component and call its addBrowseFolderListener () method to configure the file picker. As an added bonus, this will make it possible to autocomplete the file name when entering the path in the text box manually.

An alternative UI for selecting files, which is ideal when you need to search for a file by name, is available through the TreeFileChooserFactory class. The dialog box used by this API has two tabs: one shows the structure of the project, the other shows a list of files similar to those used in Goto File. To display a dialog box, call showDialog () on the object returned from createFileChooser () and then call getSelectedFile () to get a custom selection.

Class and package selection

If you want to offer the user the option of choosing a Java class, you can use the TreeClassChooserFactory class . Its various methods allow you to specify a search scope in order to limit the selection to the descendants of a particular class or interface implementations, as well as include or exclude inner classes from the list.

To select a Java package, you can use the PackageChooserDialog class .

Editor Components


Compared to Swing JTextArea, the IntelliJ IDEA editor component has many advantages - syntax highlighting, code completion, code folding, and much more. Editors in IntelliJ IDEA are usually displayed as editor tabs, but they can be embedded in dialog boxes or tool windows. This allows the EditorTextField component .

When creating an EditorTextField, you can specify the following attributes:
  • The type of file that parses the text in the field;
  • Will the text field be read-only;
  • Whether the text field is single-line or multi-line.

One common use case for EditorTextField is to edit the name of a Java class or package. This can be achieved using the following steps:
  • Use JavaCodeFragmentFactory .getInstance (). CreateReferenceCodeFragment () to create a piece of code representing the name of a class or package;
  • Call PsiDocumentManager .getInstance (). GetDocument () to get the document corresponding to the code fragment;
  • Pass the received document to the EditorTextField constructor or its setDocument () method.

Lists and trees


JBList and Tree

Whenever you plan to use the standard Swing component of JList, consider using its alternative to JBList, which supports the following additional features:
  • Rendering a tooltip containing the full text of an element if it does not fit the width of the field;
  • Drawing a gray text message in the middle of the list when it does not contain elements (the text can be configured by calling getEmptyText (). SetText ());
  • Drawing the “Busy” icon in the upper right corner of the list when a background operation is performed (enabled by calling setPaintBusy ()).

Like JBList, there is a class com.intellij.ui.treeStructure.Tree that provides a replacement for the standard JTree class. In addition to JBList features, it supports Mac-style rendering and auto-scroll for drag & drop.

ColoredListCellRenderer and ColoredTreeCellRenderer

When you need to customize the presentation of items in a combo box or tree, it is recommended that you use the ColoredListCellRenderer or ColoredTreeCellRenderer classes as a means of rendering cells. These classes allow you to collect views from several fragments of text with different attributes (by calling append ()) and apply an additional icon for the element (by calling setIcon ()). The visualization system automatically takes care of setting the correct text color for the selected elements and many other platform-specific rendering details.

ListSpeedSearch and TreeSpeedSearch

To facilitate the selection of items using the keyboard in a list or tree, you can apply a quick search handler to them. This can be done with a simple call.
or
. If you need to customize the text that is used to search for an item, you can override the getElementText () method. In addition, you can pass a function to convert elements to strings (as elementTextDelegate in the ListSpeedSearch constructor or as the toString () method in the TreeSpeedSearch constructor).

Toolbardecorator

A very common task when developing a plugin is to display a list or tree where the user could add, delete, edit or reorder items. This task is greatly facilitated by the ToolbarDecorator class . This class contains a toolbar with actions associated with items and automatically allows drag & drop to reorder items in lists if it supports the underlying list model. The position of the toolbar (above or below the list) depends on the platform on which IntelliJ IDEA is running.

To use the toolbar decorator:
  • If you need support for deleting and reordering items in a list, make sure your list model implements the EditableModel interface . CollectionListModel is a convenient class that implements this interface.
  • Call ToolbarDecorator.createDecorator to instantiate the decorator.
  • If you need support for adding and / or deleting elements, call setAddAction () and / or setRemoveAction ().
  • If you need other buttons in addition to the standard ones, call addExtraAction () or setActionGroup ().
  • Call createPanel () and add the returned component to your panel.

Other Swing Components


Messages

The Messages class provides a way to display a simple message, an input dialog (modal dialog with a text box) and a selection dialog (modal dialog with a list). The functions of the various class methods should be clear from their name. When launched on Mac OS X, message boxes use a native interface.

The showCheckboxMessageDialog () function provides an easy way to implement the “Do not show this again” check box.

It is recommended that you use non-modal notifications instead of modal message boxes, whenever appropriate. Notifications have been reviewed in one of the previous sections.

Jbsplitter

The JBSplitter class is a replacement for the standard JSplitPane class. Unlike some other improved JetBrains Swing components, this is not an equivalent replacement, as It has a different API. However, in order to achieve uniformity in the user interface, it is recommended to use JBSplitter instead of the standard JSplitPane.
To add components to the separator, call the setFirstComponent () and setSecondComponent () methods.

JBSplitter supports automatic aspect ratio storage. To enable it, call the setSplitterProportionKey () method and pass the identifier under which the proportion will be stored.

Jbtabs

The JBTabs class is a tab implementation of JetBrains used in the editor and some other components. It has a significantly different look & feel compared to the standard Swing tabs and looks more alien on the Mac OS X platform, so we leave you the choice of which tab implementation will be more suitable for your plugin.

To be continued...

All articles of the cycle: 1 , 2 , 3 , 4 , 5 , 6 , 7 .

Read Next