官方文檔
https://www.jetbrains.org/intellij/sdk/docs/user_interface_components/popups.html
https://www.jetbrains.org/intellij/sdk/docs/tutorials/action_system/grouping_action.html
Github
https://github.com/kungyutucheng/my_gradle_plugin
參考
Intellij IDEA 插件開發(fā) -- ListPopup
運(yùn)行環(huán)境
macOS 10.14.5
IntelliJ idea 2019.2.4
1齿桃、ActionGroupPopupAction
最常用呼巷,創(chuàng)建一個(gè)普通的彈出菜單
ActionGroupPopupAction
package com.kungyu.popup;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.ui.popup.*;
import org.jetbrains.annotations.NotNull;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
/**
* @author wengyongcheng
* @since 2020/3/3 10:48 下午
*/
public class ActionGroupPopupAction extends AnAction implements ListSelectionListener, JBPopupListener {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
// 單選胳搞,action被選中可以觸發(fā)對(duì)應(yīng)的actionPerformed方法
DefaultActionGroup actionGroup = (DefaultActionGroup) ActionManager.getInstance().getAction("popupGroup");
ListPopup listPopup = JBPopupFactory.getInstance().createActionGroupPopup("popup",actionGroup,e.getDataContext(), JBPopupFactory.ActionSelectionAid.SPEEDSEARCH,false);
listPopup.showInFocusCenter();
listPopup.addListSelectionListener(this);
listPopup.addListener(this);
}
/**
* 值改變事件監(jiān)聽蓝牲,通過上下鍵選擇時(shí)班缎,可以通過getFirstIndex獲取第一個(gè)選擇的元素的索引,通過getLastIndex可以獲取最后一個(gè)選擇的元素的索引
* @param e
*/
@Override
public void valueChanged(ListSelectionEvent e) {
int firstIndex = e.getFirstIndex();
int lastIndex = e.getLastIndex();
boolean valueIsAdjusting = e.getValueIsAdjusting();
System.out.println("firstIndex:" + firstIndex);
System.out.println("lastIndex:" + lastIndex);
System.out.println("valueIsAdjusting:" + valueIsAdjusting);
}
/**
* popup 監(jiān)聽彈出菜單關(guān)閉動(dòng)作
* @param event
*/
@Override
public void onClosed(@NotNull LightweightWindowEvent event) {
JBPopup jbPopup = event.asPopup();
System.out.println("關(guān)閉popup");
jbPopup.cancel();
}
}
核心方法是:
public ListPopup createActionGroupPopup(@Nullable @Nls(capitalization = Nls.Capitalization.Title)String title,
@NotNull ActionGroup actionGroup,
@NotNull DataContext dataContext,
ActionSelectionAid selectionAidMethod,
boolean showDisabledActions)
參數(shù)selectionAidMethod
代表action動(dòng)作的匹配方式菇民,可選值如下:
-
NUMBERING
:action前帶有數(shù)字序號(hào)裁着,通過選擇對(duì)應(yīng)數(shù)字即可觸發(fā)對(duì)應(yīng)action -
ALPHA_NUMBERING
:同NUMBERING
,數(shù)字不夠盹靴,字母來湊 -
SPEEDSEARCH
:快速查找炸茧,通過輸入字母實(shí)現(xiàn)模糊匹配,大小寫不敏感 -
MNEMONICS
:翻譯是幫助記憶稿静,不理解梭冠,實(shí)例也看不出來,求賜教
其中改备,showInBestPositionFor
代表由idea來選擇最佳展示位置控漠,也可以使用以下方法來自定義位置:
/**
* Shows the popup at the bottom left corner of the specified component.
*
* @param componentUnder the component near which the popup should be displayed.
*/
void showUnderneathOf(@NotNull Component componentUnder);
展示在某個(gè)組件的左下角
/**
* Shows the popup at the specified point.
*
* @param point the relative point where the popup should be displayed.
*/
void show(@NotNull RelativePoint point);
指定具體某個(gè)位置
/**
* Shows the popup in the center of the specified component.
*
* @param component the component at which the popup should be centered.
*/
void showInCenterOf(@NotNull Component component);
展示在某個(gè)組件的中間位置
/**
* Shows the popups in the center of currently focused component
*/
void showInFocusCenter();
展示在已獲取焦點(diǎn)的組件的中間位置
/**
* Shows in best position with a given owner
*/
void show(@NotNull Component owner);
展示在某個(gè)組件的最佳位置
/**
* Shows the popup in the center of the active window in the IDEA frame for the specified project.
*
* @param project the project in which the popup should be displayed.
*/
void showCenteredInCurrentWindow(@NotNull Project project);
展示在當(dāng)前項(xiàng)目的所在窗口的中間位置
注冊(cè)Action
<action id="com.kungyu.popup.ActionGroupPopupAction" class="com.kungyu.popup.ActionGroupPopupAction" text="ActionGroupPopupAction" description="ActionGroupPopupAction">
<add-to-group group-id="PopupMenuActions" anchor="first"/>
<keyboard-shortcut first-keystroke="control alt 1" keymap="Mac OS X 10.5+"/>
</action>
<group id="popupGroup"/>
<action class="com.kungyu.popup.CustomFirstAction" id="com.kungyu.popup.CustomFirstAction" text="CustomFirstAction" description="CustomFirstAction">
<add-to-group group-id="popupGroup" anchor="first"/>
</action>
<action class="com.kungyu.popup.CustomSecondAction" id="com.kungyu.popup.CustomSecondAction" text="CustomSecondAction" description="CustomSecondAction">
<add-to-group group-id="popupGroup" anchor="after" relative-to-action="CustomFirstAction"/>
</action>
CustomFirstAction
package com.kungyu.popup;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.ui.Messages;
import org.jetbrains.annotations.NotNull;
/**
* @author wengyongcheng
* @since 2020/3/4 11:06 上午
*/
public class CustomFirstAction extends AnAction {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Messages.showMessageDialog("第一個(gè)action觸發(fā)", "第一個(gè)action", Messages.getInformationIcon());
}
}
CustomSecondAction
package com.kungyu.popup;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.ui.Messages;
import org.jetbrains.annotations.NotNull;
/**
* @author wengyongcheng
* @since 2020/3/4 11:06 上午
*/
public class CustomSecondAction extends AnAction {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Messages.showMessageDialog("第二個(gè)action觸發(fā)", "第二個(gè)action", Messages.getInformationIcon());
}
}
效果
2、ComponentPopupAction
自定義彈出菜單樣式
ComponentPopupAction
package com.kungyu.popup;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.ui.popup.*;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
/**
* @author wengyongcheng
* @since 2020/3/3 10:48 下午
*/
public class ComponentPopupAction extends AnAction implements ListSelectionListener, JBPopupListener {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
// 自定義popup樣式
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
JTextField textField = new JTextField();
panel.add(textField, BorderLayout.NORTH);
JButton button = new JButton("提交");
panel.add(button, BorderLayout.CENTER);
ComponentPopupBuilder componentPopupBuilder = JBPopupFactory.getInstance().createComponentPopupBuilder(panel, textField);
JBPopup popup = componentPopupBuilder.createPopup();
popup.showInBestPositionFor(e.getDataContext());
}
/**
* 上下鍵選擇事件悬钳,僅僅是改變盐捷,而不是按住回車之后的選擇
* @param e
*/
@Override
public void valueChanged(ListSelectionEvent e) {
int firstIndex = e.getFirstIndex();
int lastIndex = e.getLastIndex();
boolean valueIsAdjusting = e.getValueIsAdjusting();
System.out.println("firstIndex:" + firstIndex);
System.out.println("lastIndex:" + lastIndex);
System.out.println("valueIsAdjusting:" + valueIsAdjusting);
}
/**
* popup 關(guān)閉監(jiān)聽
* @param event
*/
@Override
public void onClosed(@NotNull LightweightWindowEvent event) {
JBPopup jbPopup = event.asPopup();
System.out.println("關(guān)閉popup");
jbPopup.cancel();
}
}
注冊(cè)Action
<action id="com.kungyu.popup.ComponentPopupAction" class="com.kungyu.popup.ComponentPopupAction"
text="ComponentPopupAction" description="ComponentPopupAction">
<add-to-group group-id="PopupMenuActions" anchor="first"/>
<keyboard-shortcut first-keystroke="control alt 1" keymap="Mac OS X 10.5+"/>
</action>
效果
然而,我們會(huì)發(fā)現(xiàn)默勾,這個(gè)輸入框是獲取不到焦點(diǎn)的碉渡,這就很值得研究了,但是母剥,沒有什么是難得到我們的滞诺,下面我們一步步探究如何獲取到輸入框到焦點(diǎn)》?(此處环疼,不得不吐槽一句:IntelliJ的插件開發(fā)資料是真的少啊习霹,步步卡)
解決思路1:
試圖讓輸入框手動(dòng)獲取焦點(diǎn),修改代碼炫隶,增加如下語句:
textField.grabFocus();
嗯淋叶,懷著期待到心情,重新運(yùn)行等限,oh爸吮,上帝芬膝,他還是不行。
解決思路2:
從源碼抓起形娇,查看JBPopupFactory#createComponentPopupBuilder
锰霜,找到實(shí)現(xiàn)類的代碼:
@NotNull
@Override
public ComponentPopupBuilder createComponentPopupBuilder(@NotNull JComponent content, JComponent preferableFocusComponent) {
return new ComponentPopupBuilderImpl(content, preferableFocusComponent);
}
繼續(xù)跟蹤ComponentPopupBuilderImpl
類:
public ComponentPopupBuilderImpl(@NotNull JComponent component, JComponent preferredFocusedComponent) {
myComponent = component;
myPreferredFocusedComponent = preferredFocusedComponent;
}
追蹤變量myPreferredFocusedComponent
的引用,發(fā)現(xiàn)除了上述構(gòu)造函數(shù)桐早,只有另外一處使用:
@Override
@NotNull
public JBPopup createPopup() {
AbstractPopup popup = new AbstractPopup().init(
myProject, myComponent, myPreferredFocusedComponent, myRequestFocus, myFocusable, myMovable, myDimensionServiceKey,
myResizable, myTitle, myCallback, myCancelOnClickOutside, myListeners, myUseDimServiceForXYLocation, myCommandButton,
myCancelButton, myCancelOnMouseOutCallback, myCancelOnWindow, myTitleIcon, myCancelKeyEnabled, myLocateByContent,
myPlaceWithinScreen, myMinSize, myAlpha, myMaskProvider, myInStack, myModalContext, myFocusOwners, myAd, myAdAlignment,
false, myKeyboardActions, mySettingsButtons, myPinCallback, myMayBeParent,
myShowShadow, myShowBorder, myBorderColor, myCancelOnWindowDeactivation, myKeyEventHandler
);
popup.setNormalWindowLevel(myNormalWindowLevel);
popup.setOkHandler(myOkHandler);
if (myUserData != null) {
popup.setUserData(myUserData);
}
Disposer.register(ApplicationManager.getApplication(), popup);
return popup;
}
很明顯癣缅,init
方法是核心:
@NotNull
protected AbstractPopup init(Project project,
@NotNull JComponent component,
@Nullable JComponent preferredFocusedComponent,
boolean requestFocus,
boolean focusable,
boolean movable,
String dimensionServiceKey,
boolean resizable,
@Nullable String caption,
@Nullable Computable<Boolean> callback,
boolean cancelOnClickOutside,
@Nullable Set<JBPopupListener> listeners,
boolean useDimServiceForXYLocation,
ActiveComponent commandButton,
@Nullable IconButton cancelButton,
@Nullable MouseChecker cancelOnMouseOutCallback,
boolean cancelOnWindow,
@Nullable ActiveIcon titleIcon,
boolean cancelKeyEnabled,
boolean locateByContent,
boolean placeWithinScreenBounds,
@Nullable Dimension minSize,
float alpha,
@Nullable MaskProvider maskProvider,
boolean inStack,
boolean modalContext,
@Nullable Component[] focusOwners,
@Nullable String adText,
int adTextAlignment,
boolean headerAlwaysFocusable,
@NotNull List<? extends Pair<ActionListener, KeyStroke>> keyboardActions,
Component settingsButtons,
@Nullable final Processor<? super JBPopup> pinCallback,
boolean mayBeParent,
boolean showShadow,
boolean showBorder,
Color borderColor,
boolean cancelOnWindowDeactivation,
@Nullable BooleanFunction<? super KeyEvent> keyEventHandler) {
// 略
myPreferredFocusedComponent = preferredFocusedComponent;
myRequestFocus = requestFocus;
// 略
可以看到我們關(guān)心的入?yún)?code>preferredFocusedComponent被賦值給了myPreferredFocusedComponent
,查看引用:
仔細(xì)觀察哄酝,有個(gè)很明顯的獲取焦點(diǎn)語句友存,感覺離真相越來越近了,搞他:
if (myRequestFocus) {
if (myPreferredFocusedComponent != null) {
myPreferredFocusedComponent.requestFocus();
}
看到了myRequestFocus
這個(gè)變量陶衅,也就是說屡立,myRequestFocus
為true
的時(shí)候我們傳入的textField才能獲取到焦點(diǎn)。那么搀军,myRequestFocus
變量從何而來膨俐,如果足夠細(xì)心,就會(huì)發(fā)現(xiàn)是在init
方法傳入的罩句,而init
是在createPopup
中調(diào)用的焚刺,再次查看createPopup
方法:
到了這里,解決方案就很明顯了门烂,修改示例代碼如下:
popup.setRequestFocus(true);
運(yùn)行結(jié)果:
完美解決
但凡官方文檔齊全一點(diǎn)乳愉,也不至于學(xué)得這么辛苦
3、ConfirmPopupAction
確認(rèn)取消式彈出菜單
ConfirmPopupAction
package com.kungyu.popup;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.popup.*;
import org.jetbrains.annotations.NotNull;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
/**
* @author wengyongcheng
* @since 2020/3/3 10:48 下午
*/
public class ConfirmPopupAction extends AnAction implements ListSelectionListener, JBPopupListener {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
// 確認(rèn)類型popup屯远,展示倆個(gè)選項(xiàng)蔓姚,對(duì)應(yīng)執(zhí)行倆種操作,TODO 這里不知道為什么選中其中一個(gè)之后必定會(huì)先彈出no信息框
ListPopup confirmation = JBPopupFactory.getInstance().createConfirmation("confirm", "yes", "no", () -> Messages.showMessageDialog("yes", "yes", Messages.getInformationIcon()), () -> Messages.showMessageDialog("no", "no", Messages.getInformationIcon()), 0);
confirmation.showInBestPositionFor(e.getDataContext());
}
/**
* 上下鍵選擇事件氓润,僅僅是改變赂乐,而不是按住回車之后的選擇
* @param e
*/
@Override
public void valueChanged(ListSelectionEvent e) {
int firstIndex = e.getFirstIndex();
int lastIndex = e.getLastIndex();
boolean valueIsAdjusting = e.getValueIsAdjusting();
System.out.println("firstIndex:" + firstIndex);
System.out.println("lastIndex:" + lastIndex);
System.out.println("valueIsAdjusting:" + valueIsAdjusting);
}
/**
* popup 關(guān)閉監(jiān)聽
* @param event
*/
@Override
public void onClosed(@NotNull LightweightWindowEvent event) {
JBPopup jbPopup = event.asPopup();
System.out.println("關(guān)閉popup");
jbPopup.cancel();
}
}
注冊(cè)Action
<action id="com.kungyu.popup.ConfirmPopupAction" class="com.kungyu.popup.ConfirmPopupAction"
text="ConfirmPopupAction" description="ConfirmPopupAction">
<add-to-group group-id="PopupMenuActions" anchor="first"/>
<keyboard-shortcut first-keystroke="control alt 1" keymap="Mac OS X 10.5+"/>
</action>
4、MultiChoosePopupAction
多選式彈出菜單
MultiChoosePopupAction
package com.kungyu.popup;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.ui.popup.*;
import org.jetbrains.annotations.NotNull;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.text.Collator;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author wengyongcheng
* @since 2020/3/3 10:48 下午
*/
public class MultiChoosePopupAction extends AnAction implements ListSelectionListener, JBPopupListener {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
// 可以多選咖气,如果傳入的list是action挨措,選中不會(huì)觸發(fā)對(duì)應(yīng)的actionPerformed方法
IPopupChooserBuilder<String> popupChooserBuilder = JBPopupFactory.getInstance().createPopupChooserBuilder(Stream.of("value1","value2").collect(Collectors.toList()));
JBPopup popup = popupChooserBuilder.createPopup();
popup.showInBestPositionFor(e.getDataContext());
}
/**
* 上下鍵選擇事件,僅僅是改變崩溪,而不是按住回車之后的選擇
* @param e
*/
@Override
public void valueChanged(ListSelectionEvent e) {
int firstIndex = e.getFirstIndex();
int lastIndex = e.getLastIndex();
boolean valueIsAdjusting = e.getValueIsAdjusting();
System.out.println("firstIndex:" + firstIndex);
System.out.println("lastIndex:" + lastIndex);
System.out.println("valueIsAdjusting:" + valueIsAdjusting);
}
/**
* popup 關(guān)閉監(jiān)聽
* @param event
*/
@Override
public void onClosed(@NotNull LightweightWindowEvent event) {
JBPopup jbPopup = event.asPopup();
System.out.println("關(guān)閉popup");
jbPopup.cancel();
}
}
注冊(cè)Action
<action id="com.kungyu.popup.MultiChoosePopupAction" class="com.kungyu.popup.MultiChoosePopupAction"
text="MultiChoosePopupAction" description="MultiChoosePopupAction">
<add-to-group group-id="PopupMenuActions" anchor="first"/>
<keyboard-shortcut first-keystroke="control alt 1" keymap="Mac OS X 10.5+"/>
</action>
效果
5浅役、SubGroupPopupAction
子菜單式彈出菜單
SubGroupPopupAction
package com.kungyu.popup;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.ui.popup.*;
import org.jetbrains.annotations.NotNull;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
/**
* @author wengyongcheng
* @since 2020/3/3 10:48 下午
*/
public class SubGroupPopupAction extends AnAction implements ListSelectionListener, JBPopupListener {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
// 嵌套action
DefaultActionGroup actionGroup = (DefaultActionGroup) ActionManager.getInstance().getAction("subOuterGroup");
ListPopup listPopup = JBPopupFactory.getInstance().createActionGroupPopup("SubOuterGroup",actionGroup,e.getDataContext(), JBPopupFactory.ActionSelectionAid.SPEEDSEARCH,false);
listPopup.showInFocusCenter();
listPopup.addListSelectionListener(this);
listPopup.addListener(this);
}
/**
* 上下鍵選擇事件,僅僅是改變伶唯,而不是按住回車之后的選擇
* @param e
*/
@Override
public void valueChanged(ListSelectionEvent e) {
int firstIndex = e.getFirstIndex();
int lastIndex = e.getLastIndex();
boolean valueIsAdjusting = e.getValueIsAdjusting();
System.out.println("firstIndex:" + firstIndex);
System.out.println("lastIndex:" + lastIndex);
System.out.println("valueIsAdjusting:" + valueIsAdjusting);
}
/**
* popup 關(guān)閉監(jiān)聽
* @param event
*/
@Override
public void onClosed(@NotNull LightweightWindowEvent event) {
JBPopup jbPopup = event.asPopup();
System.out.println("關(guān)閉popup");
jbPopup.cancel();
}
}
注冊(cè)Action
<action id="com.kungyu.popup.SubGroupPopupAction" class="com.kungyu.popup.SubGroupPopupAction"
text="SubGroupPopupAction" description="SubGroupPopupAction">
<add-to-group group-id="PopupMenuActions" anchor="first"/>
<keyboard-shortcut first-keystroke="control alt 1" keymap="Mac OS X 10.5+"/>
</action>
<group id="subOuterGroup"/>
<!-- popup為true代表這是一個(gè)子菜單 -->
<group id="subInnerGroup" popup="true" text="SubInnerGroup">
<add-to-group group-id="subOuterGroup" anchor="first"/>
<action class="com.kungyu.popup.SubAction" id="subAction"
text="SubAction" description="SubAction">
</action>
</group>
SubAction
package com.kungyu.popup;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.ui.Messages;
import org.jetbrains.annotations.NotNull;
/**
* @author wengyongcheng
* @since 2020/3/4 5:28 下午
*/
public class SubAction extends AnAction {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Messages.showMessageDialog("nest", "nest", Messages.getInformationIcon());
}
}