(十八)IntelliJ 插件开发——Preference Setting(首选项配置)

Github

https://github.com/kungyutucheng/my_gradle_plugin

参考

Easy Code

运行环境

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实现了俩个接口:ConfigurableConfigurable.Composite

  1. Configurable接口这里实现了以下几个方法:
  • getDisplayName:展示的名称
  • isModified:当数据修改时会触发该方法,返回值代表Apply按钮是否可用,该Demo中通过判断原先的配置origSetting与新的输入内容newSetting是否相同来控制Apply按钮是否可用
  • apply:点击Apply按钮后触发,可以用来保存数据
  • reset:重置,通过Reset按钮触发,无须多言
  1. 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;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容