Github
https://github.com/kungyutucheng/my_gradle_plugin
参考
运行环境
macOS 10.14.5
IntelliJ idea 2019.2.4
定义
官方文档未找到如何在Preference中添加自定义的设置,所以在网上搜索到Easy Code
项目,通过观看其源码撸了以下的Demo代码。
效果
Demo
1、新建数据储存Settings
package com.kungyu.setting;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
/**
* @author wengyongcheng
* @since 2020/3/14 10:27 下午
*/
@State(name = "MainSetting", storages = @Storage("main-setting.xml"))
public class Settings implements PersistentStateComponent<Settings> {
private Map<String, String> settingMap;
public Map<String, String> getSettingMap() {
return settingMap;
}
public void setSettingMap(Map<String, String> settingMap) {
this.settingMap = settingMap;
}
public static Settings getInstance() {
return ServiceManager.getService(Settings.class);
}
public Settings(){
init();
}
private void init() {
settingMap = new HashMap<>();
}
@Nullable
@Override
public Settings getState() {
return this;
}
/**
* 新的组件状态被加载时,调用该方法,如果IDE运行期间,保存数据的文件被从外部修改,则该方法会被再次调用
* @param state
*/
@Override
public void loadState(@NotNull Settings state) {
setSettingMap(state.getSettingMap());
}
}
@State(name = "MainSetting", storages = @Storage("main-setting.xml"))
@State
注解用来定义配置数据存放的具体路径,具体请戳(二十一)IntelliJ 插件开发——Persisting State of Components(组件的持久化状态)
Settings
实现了PersistentStateComponent
接口,该接口需要实现俩个方法:
-
getState
:返回当前组件的state
/**
* @return a component state. All properties, public and annotated fields are serialized. Only values, which differ
* from the default (i.e., the value of newly instantiated class) are serialized. {@code null} value indicates
* that the returned state won't be stored, as a result previously stored state will be used.
* @see com.intellij.util.xmlb.XmlSerializer
*/
@Nullable
T getState();
-
loadState
:新的组件状态被加载时,调用该方法,如果IDE运行期间,保存数据的文件被从外部修改,则该方法会被再次调用
/**
* This method is called when new component state is loaded. The method can and will be called several times, if
* config files were externally changed while IDE was running.
* <p>
* State object should be used directly, defensive copying is not required.
*
* @param state loaded component state
* @see com.intellij.util.xmlb.XmlSerializerUtil#copyBean(Object, Object)
*/
void loadState(@NotNull T state);
2、创建GUI Form对象MainSetting,作为设置主入口
package com.kungyu.setting;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurationException;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
/**
* @author wengyongcheng
* @since 2020/3/14 10:16 下午
*/
public class MainSetting implements Configurable,Configurable.Composite {
private JTextField textField;
private JLabel label;
private JPanel mainPanel;
private Settings settings = Settings.getInstance();
public MainSetting(){init();}
private void init() {
this.settings = Settings.getInstance();
textField.setText(settings.getSettingMap().get("mainSetting"));
}
@Nls(capitalization = Nls.Capitalization.Title)
@Override
public String getDisplayName() {
return "Main Setting";
}
/**
* 通过方法返回定义的子设置组件
* @return
*/
@NotNull
@Override
public Configurable[] getConfigurables() {
Configurable[] configurables = new Configurable[1];
configurables[0] = new SubSetting();
return configurables;
}
@Nullable
@Override
public JComponent createComponent() {
return mainPanel;
}
/**
* 设置apply按钮是否可用,数据修改时被调用
* @return
*/
@Override
public boolean isModified() {
String origSetting = settings.getSettingMap().get("mainSetting");
String newSetting = textField.getText();
return !StringUtils.equals(origSetting,newSetting);
}
/**
* 点击apply按钮后被调用
* @throws ConfigurationException
*/
@Override
public void apply() throws ConfigurationException {
settings.getSettingMap().put("mainSetting", textField.getText());
}
/**
* reset按钮被点击时触发
*/
@Override
public void reset() {
init();
}
}
GUI Form
创建方法请戳(十六)IntelliJ 插件开发——Run Configuration(运行配置)
从代码中我们可以看到,MainSetting
实现了俩个接口:Configurable
和Configurable.Composite
-
Configurable
接口这里实现了以下几个方法:
-
getDisplayName
:展示的名称 -
isModified
:当数据修改时会触发该方法,返回值代表Apply
按钮是否可用,该Demo中通过判断原先的配置origSetting
与新的输入内容newSetting
是否相同来控制Apply
按钮是否可用 -
apply
:点击Apply
按钮后触发,可以用来保存数据 -
reset
:重置,通过Reset
按钮触发,无须多言
-
Configurable.Composite
里面只有一个方法:
-
getConfigurables
:用来返回该组件下的子组件列表,这里我们返回了自定义的SubSetting
3、子组件SubSetting
package com.kungyu.setting;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurationException;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
/**
* @author wengyongcheng
* @since 2020/3/14 10:20 下午
*/
public class SubSetting implements Configurable {
private JPanel mainPanel;
private JTextField testField;
private JLabel label;
public SubSetting(){}
@Nls(capitalization = Nls.Capitalization.Title)
@Override
public String getDisplayName() {
return "Sub Setting";
}
@Nullable
@Override
public JComponent createComponent() {
return mainPanel;
}
@Override
public boolean isModified() {
return false;
}
@Override
public void apply() throws ConfigurationException {
}
}
与MainSetting
一致,唯一的区别时没有实现Configurable.Composite
4、注册service和配置
<extensions defaultExtensionNs="com.intellij">
<applicationService serviceImplementation="com.kungyu.setting.Settings"/>
<applicationConfigurable instance="com.kungyu.setting.MainSetting" dynamic="true"/>
</extensions>
其中,dynamic
属性表明这是一个继承了Configurable.Composite
接口的配置组件,并且其配置子项需要通过调用Configurable.Composite#getConfigurables
方法来动态实现,这种动态加载的好处是通过不加载额外的配置来避免性能消耗,原文如下:
/**
* This attribute states that a custom configurable component implements the {@link Configurable.Composite} interface
* and its children are dynamically calculated by calling the {@link Configurable.Composite#getConfigurables()} method.
* It is needed to improve performance, because we do not want to load any additional classes during the building a setting tree.
*/
@Attribute("dynamic")
public boolean dynamic;