This chapter mainly explains the core implementation of AnAction. For more configuration, please refer to the previous chapters. The action system of Idea-IDE allows plug-ins to add Actions to the IDE menu and toolbar based on the IntelliJ platform. As shown in the figure below, Actions can be added to tools separately In the bar or right-click menu, it can also be added to the Group first, and then added in the Group mode.
- AnAction or ActionGroup: three places that can be added to the IDE;
- An ActionGroup can contain multiple AnActions;
- An AnAction can be added to multiple ActionGroups;
1. AnAction
A custom Action only needs two operations in real time: 1. Configure the action definition in plugin.xml (of course, it can also be implemented by hard coding, but it is not recommended); 2. Implement the subclass of the custom AnAction.
1. Description of class AnAction
The AnAction class cannot add any type of class field during implementation to prevent memory leaks.
? To implement a custom Action, inherit the AnAction class and rewrite the actionPerformed() core method. It is recommended to also rewrite the update() method. There are three methods that are often used are explained in detail as follows:
- AnAction.actionPerformed(): Called by the IntelliJ platform to perform the most complex operations of the plugin. It receives a parameter named AnActionEvent, this parameter can be used to access any contextual data such as project, file, selection, etc.;
- AnAction.update(): Called by the IntelliJ platform to update the status of the action, such as whether the action is enabled, visible, etc. Be careful not to perform time-consuming operations in the rewritten code. Get the Presentation object through AnActionEvent.getPresentation() (the default Presentation object is a set of description information about menu or toolbar operations), and then execute the state of the object associated with the event context through this object. Another point is that the same Action can have different text or icons in different user interface positions, which is also achieved by cloning the Presentation object through AnAction. Replace the default text with override-text).
- AnAction.getActionUpdateThread(): This method (only supported in 2022.3 +) returns an ActionUpdateThread object. It has two execution schemes: execute the update() method on the BGT background thread or the EDT event scheduling thread. The advantage of the former is that it can maintain access to data models such as PSI and VFS during execution, but cannot access the Swing component structure. And EDT is just the opposite.
2. Configuration instructions
<actions> <action id="com.example.impl.CollectGarbage" class="com.example.impl.CollectGarbage" text="Garbage Collector: Collect _Garbage" description="Run garbage collector" icon="icons/garbage.png"> </action> <actions>
Several important attribute descriptions:
- id: a unique value within a certain range, it is recommended to have the same name as the implementation class, or refer to the source code implementation of idea implementation;
- ?text: The text of the action displayed on the UI interface. If internationalization is used, the key attribute needs to be used instead of the text attribute
- icon: A .png image is used in this example, but it is recommended to use all .svg format files;
Child node
Among them, synonym is used more, which defines the keyword to find this action in Help | Find Action. If searching is prohibited, searchable=”false” can be set. It also supports the internationalization of the key method.
<action id="VssIntegration. GarbageCollection" class="com.example.impl.CollectGarbage" text="Garbage Collector: Collect _Garbage" description="Run garbage collector" icon="icons/garbage.png"> <!-- The second <override-text> element uses the alternate attribute "use-text-of-place" to define a location (EditorPopup) to use the same text as is used in MainMenu. It is a way to specify the use of an alternate menu text in multiple discrete menu groups. --> <override-text place="MainMenu" text="Collect _Garbage"/> <override-text place="EditorPopup" use-text-of-place="MainMenu"/> <!-- Provide alternative names for searching action by name --> <synonym text="GC"/> <add-to-group group-id="ToolsMenu" relative-to-action="GenerateJavadoc" anchor="after"/> <!-- Add the first and second keystrokes to all keymaps... --> <keyboard-shortcut keymap="$default" first-keystroke="control alt G" second-keystroke="C"/> <!-- ...except the "Mac OS X" keymap and its children. --> <keyboard-shortcut keymap="Mac OS X" first-keystroke="control alt G" second-keystroke="C" remove="true"/> <!-- The "Mac OS X 10.5 + " keymap and its children will have only this keyboard shortcut for this action. --> <keyboard-shortcut keymap="Mac OS X 10.5 + " first-keystroke="control alt G" second-keystroke="C" replace-all="true"/> <mouse-shortcut keymap="$default" keystroke="control button3 double click"/> </action>
I18N settings
The recommended configuration method is to define a resource file for the entire plugin, but it also supports defining a separate resource file for the action. The configuration is as follows (see the end of this chapter for usage):
Define resource files for plugins
/resources /messages /BasicActionsBundle.properties <resource-bundle>messages.BasicActionsBundle</resource-bundle>
Separately define resource files for actions
<actions resource-bundle="messages.MyActionsBundle"> <!-- action/group defined here will use keys from MyActionsBundle.properties --> </actions>
The suggested resource file format is as follows. For the
//Actions action.<action-id>.text=Translated Action Text action.<action-id>.description=Translated Action Description //Actions. override-text action.<action-id>.<place>.text=Place-dependent Translated Action Text //Groups group.<group-id>.text=Translated Group Text group.<group-id>.description=Translated Group Description //Groups. override-text group.<group-id>.<place>.text=Place-dependent Translated Group Text
3. Build complex UI from Actions?
If a plug-in needs to include a toolbar or popup menu in the user interface, which is built from a set of actions, it can be achieved through ActionPopupMenu and ActionToolbar. These objects can be created by calling the ActionManager.createActionPopupMenu() and createActionToolbar() methods. Call the corresponding getComponent() method to get the Swing component.
If the action toolbar needs to be attached to a specific component (for example, a panel in a tool window), call ActionToolbar.setTargetComponent() and pass an instance of the relevant component as a parameter. Setting a target ensures that the state of a toolbar button depends on the state of the related component, rather than on the current focus position within the IDE frame.
Two,
When implemented through .xml, the group is static, and the group cannot be changed during the running of the IDE, but its status can be set whether it is available or not. Dynamic groups can also be created programmatically.
1. Static group
<group class="com.example.impl.MyActionGroup" id="TestActionGroup" text="Test Group" description="Group with test actions" icon="icons/testgroup.png" popup="true" compact="true"> <action id="VssIntegration.TestAction" class="com.example.impl.TestAction" text="My Test Action" description="My test action"/> <!-- The <separator> element defines a separator between actions. It can also have an <add-to-group> child element. --> <separator/> <group id="TestActionSubGroup"/> <!-- The <reference> element allows adding an existing action to the group. The mandatory "ref" attribute specifies the ID of the action to add. --> <reference ref="EditorCopy"/> <add-to-group group-id="MainMenu" relative-to-action="HelpMenu" anchor="before"/> </group>
Several important properties:
- popup: Whether the operation in the group allows adding sub-actions;
- compact: Whether operations in this group are visible when disabled
2. Dynamic group
The limitation is that
public class DynamicActionGroup extends ActionGroup { @NotNull @Override public AnAction[] getChildren(AnActionEvent event) { return new AnAction[]{ new PopupDialogAction( "Action Added at Runtime", "Dynamic Action Demo", SdkIcons.Sdk_default_icon) }; } }
3. Extend DefaultActionGroup?
More advanced features can be implemented, such as conditionally showing or hiding groups. This needs to extend the implementation class of DefaultActionGroup of ActionGroup. The DefaultActionGroup is used to add sub-actions and separators between them to the group. Use this class if the set of operations belonging to the group will not change at runtime.
public class CustomDefaultActionGroup extends DefaultActionGroup { @Override public void update(AnActionEvent event) { // Enable/disable depending on whether a user is editing Editor editor = event. getData(CommonDataKeys. EDITOR); event.getPresentation().setEnabled(editor != null); // Take this opportunity to set an icon for the group. event.getPresentation().setIcon(SdkIcons.Sdk_default_icon); } }
Register a custom group
<resource-bundle>messages.BasicActionsBundle</resource-bundle> <actions> <group id="org.intellij.sdk.action.CustomDefaultActionGroup" class="org.intellij.sdk.action.CustomDefaultActionGroup" popup="true"> <add-to-group group-id="EditorPopupMenu" anchor="first"/> </group> </actions>
3. Example
In this example, an Action will be implemented, and then placed in toolWindow, group, and popup respectively through configuration.
1. Basic implementation
Implement Action
public class PopupDialogAction extends AnAction { //No variables can be defined here to prevent oom public PopupDialogAction() { super(); } public PopupDialogAction(@Nullable String text, @Nullable String description, @Nullable Icon icon) { super(text, description, icon); } @Override public void actionPerformed(@NotNull AnActionEvent event) { // get the current item Project currentProject = event. getProject(); // Get some description information from the Presentation context StringBuilder dlgMsg = new StringBuilder(event. getPresentation(). getText() + "Selected!"); String dlgTitle = event. getPresentation(). getDescription(); //The currently browsed object Navigatable nav = event.getData(CommonDataKeys.NAVIGATABLE); if (nav != null) { dlgMsg.append(String.format("\\ Selected Element: %s", nav.toString())); } //Display a Dialog Messages.showMessageDialog(currentProject, dlgMsg.toString(), dlgTitle, Messages.getInformationIcon()); } @Override public void update(AnActionEvent e) { // Set the availability based on whether a project is open Project project = e. getProject(); e.getPresentation().setEnabledAndVisible(project != null); } }
Define the plugin icon
public class SdkIcons { //The default icon file is stored in the src/main/resources/icons/ directory public static final Icon Sdk_default_icon = IconLoader.getIcon("/icons/sdk_16.svg", SdkIcons.class); }
Display 1: Add to toolWindow
<action id="org.intellij.sdk.action.PopupDialogAction" class="org.intellij.sdk.action.PopupDialogAction" text="Action Basics Plugin: Pop Dialog Action" description="SDK action example" icon="SdkIcons.Sdk_default_icon"> <add-to-group group-id="ToolsMenu" anchor="first"/> <override-text place="MainMenu" text="Pop Dialog Action"/> <keyboard-shortcut first-keystroke="control alt A" second-keystroke="C" keymap="$default"/> <mouse-shortcut keystroke="control button3 doubleClick" keymap="$default"/> </action>
[Note]: The usage of
Display 2: add to toolWindow in group mode
<group id="org.intellij.sdk.action.GroupedActions" text="Static Grouped Actions" description="SDK statically grouped action example" popup="true" icon="SdkIcons.Sdk_default_icon"> <add-to-group group-id="ToolsMenu" anchor="after" relative-to-action="org.intellij.sdk.action.PopupDialogAction"/> <action id="org.intellij.sdk.action.GroupPopDialogAction" class="org.intellij.sdk.action.PopupDialogAction" text="A Group Action" description="SDK static grouped action example" icon="SdkIcons.Sdk_default_icon"> </action> </group>
[Note]: The value of
Display 3: Add to popup (refer to internationalization implementation)
<group id="org.intellij.sdk.action.CustomDefaultActionGroup" class="org.intellij.sdk.action.CustomDefaultActionGroup" popup="true"> <add-to-group group-id="EditorPopupMenu" anchor="first"/> <action id="org.intellij.sdk.action.CustomGroupedAction" class="org.intellij.sdk.action.PopupDialogAction" icon="SdkIcons.Sdk_default_icon"/> </group>
public class CustomDefaultActionGroup extends DefaultActionGroup { @Override public void update(AnActionEvent event) { // Enable/disable depending on whether user is editing Editor editor = event. getData(CommonDataKeys. EDITOR); event.getPresentation().setEnabled(editor != null); // Take this opportunity to set an icon for the group. event.getPresentation().setIcon(SdkIcons.Sdk_default_icon); } }
2. Realization of dynamic group
<group id="org.intellij.sdk.action.DynamicActionGroup" class="org.intellij.sdk.action.DynamicActionGroup" popup="true" text="Dynamically Grouped Actions" description="SDK dynamically grouped action example" icon="SdkIcons.Sdk_default_icon"> <add-to-group group-id="ToolsMenu" anchor="after" relative-to-action="org.intellij.sdk.action.GroupedActions"/> </group>
The java implementation is as follows, where the action definition must be hard-coded
public class DynamicActionGroup extends ActionGroup { @Override public AnAction @NotNull [] getChildren(AnActionEvent e) { return new AnAction[]{ new PopupDialogAction("Action Added at Runtime", "Dynamic Action Demo", SdkIcons. Sdk_default_icon) }; } }
3. Automatic matching I18 implementation
In addition to specifying the key attribute, idea’s I18N implementation can also be implemented through the internal logic of the internationalized resource file (this method will save some code), see the configuration of CustomDefaultActionGroup above. In this configuration, the necessary attributes text, description, and key are not configured. But the program will not report an error. The reasons are as follows:
<resource-bundle>messages.BasicActionsBundle</resource-bundle>
action.org.intellij.sdk.action.CustomGroupedAction.text=A Popup Action [EN] action.org.intellij.sdk.action.CustomGroupedAction.description=SDK popup grouped action example [EN] group.org.intellij.sdk.action.CustomDefaultActionGroup.text=Popup Grouped Actions [EN] group.org.intellij.sdk.action.CustomDefaultActionGroup.description=Custom defaultActionGroup demo [EN]
<group id="org.intellij.sdk.action.CustomDefaultActionGroup" class="org.intellij.sdk.action.CustomDefaultActionGroup" popup="true"> <add-to-group group-id="EditorPopupMenu" anchor="first"/> <action id="org.intellij.sdk.action.CustomGroupedAction" class="org.intellij.sdk.action.PopupDialogAction" icon="SdkIcons.Sdk_default_icon"/> </group>
For example, in the configuration line action.org.intellij.sdk.action.CustomGroupedAction.text=A Popup Action [EN], the previous action and the middle id are location information:
- action: Indicates
in the configuration file - org.intellij.sdk.action.CustomGroupedAction: Indicates the id attribute value of
- text: Indicates the value to be set for the text of
;