Фон Задача заключалась в том, чтобы создать удобный способ для разработчиков и QA запускать около 20 серверных приложений, живущих в общем репозитории (Spring с XML-конфигурацией и общим для всех частей приложения загрузочным классом).
Как сделать что-то удобное для человека, который последний раз рисовал графический интерфейс в Borland Delphi 6.0? Возьмите что-то уже готовое и адаптируйте под свои нужды, а поскольку в IntelliJ Idea работают будущие пользователи, возникла идея создать плагин, который бы выглядел и вел себя так же, как это делает Maven Integration Plugin.
Ниже приведены классы и некоторые утилитарные методы, которые помогут вам это сделать.
Несколько слов о документации Документацию по плагинам для Idea можно найти здесь или в серии статей о хабре .
Единственная проблема с этой документацией заключается в том, что в ней почти ничего нет о пользовательском интерфейсе, к счастью, есть код , и это лучше любой документации! (особенно если у вас первый ранг в лопате) Нас, конечно, интересует папка плагины/Maven .
Секреты и уловки
Окно инструментов
Чтобы добавить документацию к окну инструментов предложения используйте плагин.xml. Но если вам нужно, например, контролировать время инициализации окна, то все это придется делать вручную:
private void initToolWindow() { final ToolWindowManagerEx manager = ToolWindowManagerEx.getInstanceEx(myProject); ToolWindowEx myToolWindow = (ToolWindowEx) manager.registerToolWindow(HolyProjectPanel.ID, false, ToolWindowAnchor.RIGHT, myProject, true); myToolWindow.setIcon(IconLoader.findIcon("/icons/jesterhat.png")); final ContentFactory contentFactory = ServiceManager.getService(ContentFactory.class); final Content content = contentFactory.createContent(panel, "", false); ContentManager contentManager = myToolWindow.getContentManager(); contentManager.addContent(content); }
Как дождаться, пока Idea полностью инициализирует проект?
Ничего сложного, если вы найдете пример кода: public static void runWhenInitialized(final Project project, final Runnable r) {
if (project.isDisposed()) return;
if (!project.isInitialized()) {
StartupManager.getInstance(project).
registerPostStartupActivity(DisposeAwareRunnable.create(r, project));
return;
}
runDumbAware(project, r);
}
public static void runDumbAware(final Project project, final Runnable r) {
if (DumbService.isDumbAware(r)) {
r.run();
}
else {
DumbService.getInstance(project).
runWhenSmart(DisposeAwareRunnable.create(r, project));
}
}
Как вырастить дерево
Прежде чем вырастить дерево, нужно понять, где это сделать.
Ответ очевиден: JPanel (упомянутая выше HolyProjectPanel является подклассом SimpleToolWindowPanel, который наследуется от JPanel (яйцо в утке, утка в зайце, заяц в шоке) .
Мы создаем объект JTree и сохраняем его как контент. Плагин UI Maven построен на классе SimpleTree, его использование ничем не отличается от JTree, но добавляет полезные функции, например, поиск на лету:
Как наполнить дерево
Заполнение предлагается делать с помощью SimpleTreeBuilder: SimpleTreeBuilder myTreeBuilder = new SimpleTreeBuilder(tree, (DefaultTreeModel) tree.getModel(), this, null)
Disposer.register(project, myTreeBuilder);
myTreeBuilder.initRoot();
myTreeBuilder.expand(myRoot, null);
Помимо прочего, он позволяет сортировать узлы, просто передавая Comparator в качестве последнего аргумента конструктора.
Также часто бывает полезно обновить все узлы, начиная с этого: myTreeBuilder.addSubtreeToUpdate(node)
Чем наполнить дерево
По аналогии с SimpleTree и SimpleTreeBuilder мы будем использовать SimpleNode. Этот класс прост как три копейки.Необходимо реализовать всего один метод getChildren, который вызывается каждый раз, когда необходимо отрисовать узел (при открытии всего окна или при расширении поддерева), и несколько доступных полей с очевидными именами, которые легко найти с помощью автодополнения.
(myName, myClosedIcon и т. д.).
Эта кажущаяся простота заканчивается, когда вы начинаете вникать в детали.
Как написать название узла разными цветами
Класс SimpleNode отображается с использованием объектов класса PresentationData. это то, что вы должны использовать:
private void updatePresentation() {
PresentationData presentation = getPresentation();
presentation.clear();
presentation.addText(myName, SimpleTextAttributes.REGULAR_ATTRIBUTES);
presentation.addText(" Red", new SimpleTextAttributes(Font.PLAIN, Color.RED));
presentation.addText(" Level2Node", SimpleTextAttributes.GRAYED_BOLD_ATTRIBUTES);
}
Помимо прочего, для перерисовки узла необходимо использовать тот же объект:
@Override
public void handleDoubleClickOrEnter(SimpleTree tree, InputEvent inputEvent) {
if(Color.RED.equals(myColor)){
myColor = Color.BLUE;
} else {
myColor = Color.RED;
}
updatePresentation();
}
private void updatePresentation() {
PresentationData presentation = getPresentation();
presentation.clear();
presentation.addText(myName, SimpleTextAttributes.REGULAR_ATTRIBUTES);
presentation.addText(" Red", new SimpleTextAttributes(Font.PLAIN, myColor));
presentation.addText(" Level2Node", SimpleTextAttributes.GRAYED_BOLD_ATTRIBUTES);
}
Почему дерево рушится каждый раз, когда я закрываю и открываю панель?
Это происходит потому, что все узлы, кроме корневого, фактически создаются заново.Чтобы избежать этого, используйте класс CachingSimpleNode вместо SimpleNode (у него также есть единственный метод, который необходимо реализовать: buildChildren()).
Чтобы вручную перерисовать CachingSimpleNode, вам необходимо вызвать для него метод update() или update(PresentationData).
Как добавить кнопки над деревом
Способ добавления кнопок мне показался не совсем очевидным (тем более, если сам ToolsWindow создавать в коде).
Они добавляются в файле plugin.xml в разделе «Действия», где настраиваются названия, значки, подсказки и все остальное.
<actions>
<action id="HolyProject.ExpandAll" class="ui.ProcessesTreeAction$ExpandAll" text="Expand All"
icon="AllIcons.Actions.Expandall"/>
<action id="HolyProject.CollapseAll" class="ui.ProcessesTreeAction$CollapseAll" text="Collapse All"
icon="AllIcons.Actions.Collapseall"/>
<action class="actions.MyToggleAction" id="HolyProject.RefreshProcesses" icon="AllIcons.Nodes.Cvs_roots"
text="Refresh HolyProjectProcesses status" description="Refresh HolyProjectProcesses status"/>
<action class="actions.CommonAction" id="HolyProject.RecreateProcesses" icon="AllIcons.ToolbarDecorator.Import"
text="Recreate Processes List" description="Recreate Processes List"/>
<group id="HolyProject.ProcessesToolbar">
<reference id="HolyProject.RefreshProcesses"/>
<reference id="HolyProject.RecreateProcesses"/>
<separator/>
<reference id="HolyProject.ExpandAll"/>
<reference id="HolyProject.CollapseAll"/>
</group>
</actions>
Чтобы добавить кнопку «Включено/Отключено», например «Переключить режим пропуска тестов», вам нужно использовать ToggleAction вместо AnAction.
Как вызвать какое-либо действие в потоке пользовательского интерфейса из другого потока
Это не имеет прямого отношения к дизайну панели, но слишком часто возникает необходимость вызвать действие, которое должно быть выполнено в потоке пользовательского интерфейса, откуда-то еще.
Для этого есть метод: AppUIUtil.invokeOnEdt(() -> {/*do smth useful*/});
Это все, чем я хотел поделиться, у меня есть пример плагина на github .
Удачи! Теги: #intellij idea #плагины #разработка #сапожник в сапогах #программирование #java
-
Ботаника
19 Oct, 24 -
Новый Взгляд На Хранение Файлов И Ссылок
19 Oct, 24 -
Авторские Права: Мысли Вслух, Часть 1
19 Oct, 24 -
Доведение Css До Пост-Загрузки
19 Oct, 24 -
Первый Хакатон В Альфа-Банке. Со Сцены
19 Oct, 24