IntelliJ IDE 插件开发 | (三)消息通知与事件监听

news2024/11/17 14:34:13

系列文章

  • IntelliJ IDE 插件开发 |(一)快速入门
  • IntelliJ IDE 插件开发 |(二)UI 界面与数据持久化
  • IntelliJ IDE 插件开发 |(三)消息通知与事件监听

前言

在前两篇文章中讲解了关于插件开发的基础知识,本文将介绍关于消息通知和事件监听方面的内容,关于 IntelliJ IDE 插件开发的基本内容也就到此为止,在下一篇文章中将开发一个简单的插件对这部分内容做一个总结,后续再介绍关于虚拟文件和PSI相关的知识,最后以一个代码生成插件作为结尾。话不多说,下面就开始本文的主题,同时本文涉及到的的完整代码已上传到Github。

消息通知

在 IntelliJ IDE 中用于展示消息提示的方式有很多种,例如侧边栏的消息通知、对话框、鼠标悬浮代码时出现的弹框等等,下面就一一进行介绍。

侧边栏通知

这种通知是在 IDE 内的右下角进行显示,同时这类消息会集中收集在消息通知中可进行查看(如下图所示),并且还可以让用户在Settings | Appearance & Behavior | Notifications中设置。

image-20231210171737691

这种通知可使用Notifications.Bus.notify()方法或NotificationGroupManager类来进行实现,例如上面的例子如果使用Notifications.Bus.notify()方法就是:

val notification = Notification("listener", "Hello, world!", NotificationType.INFORMATION)
Notifications.Bus.notify(notification, e.project)

可以看到需要传递两个参数,一个是通知对象,一个是项目对象(可选)。不过核心还是Notification这个通知类,其中推荐使用的构造方式有两种:

  • Notification(String groupId, String content, NotificationType type)
  • Notification(String groupId, String title, String content, NotificationType type)

在上述例子里使用的则是第一个构造参数方式,其中第一个参数是分组 id,可以使用系统的已有的分组,也可以选择自己创建分组,这里还是推荐自行创建,只要在 plugin.xml 中加入如下配置即可:

<extensions defaultExtensionNs="com.intellij">
    <notificationGroup id="listener" displayType="BALLOON"/>
</extensions>

其中配置的 id 就可以使用了。

对于第二参数则是我们要显示的内容,如果选择的是第二个构造函数,则可以设置消息的标题,效果如下:

image-20231210190316090

这里的内容也支持使用 HTML 标签:

val content = """
    <h4>四级标题<h1>
    <p>第一段原色字体</p>
    <p style="color: blue;">第二段蓝色字体</p>
""".trimIndent()
val notification = Notification("listener", "Title", content, NotificationType.INFORMATION)
Notifications.Bus.notify(notification, e.project)

对应的效果如下:

image-20231210192232537

最后一个参数则是消息的类型,总共有四种IDE_UPDATEINFORMATIONWARNINGERROR,效果如下图所示:

image-20231210190759222

同样地,如果使用NotificationGroupManager类,则按照以下方式使用即可,其中 Title 参数也是可选项:

NotificationGroupManager.getInstance()
    .getNotificationGroup("listener")
    .createNotification("Title", "Hello, world!", NotificationType.INFORMATION)
    .notify(e.project)

除了以上设置内容之外,相信你也经常可以看到类似如下包含可点击内容的消息通知:

timed_toast@2x

可以通过 Notification 类的addAction方法(可添加多个 Action )实现类似的效果:

val notification = Notification("listener", "Title", "Hello, world!", NotificationType.INFORMATION)
notification.addAction(object : NotificationAction("ShowProjectStructureSettings") {
    override fun actionPerformed(e: AnActionEvent, notification: Notification) {
        // 用于打开设置界面, 这里的 ShowSettings 是 IDEA 定义的常量值
        ActionManager.getInstance().getAction("ShowSettings").actionPerformed(e)
    }
})
Notifications.Bus.notify(notification, e.project)

最终效果如下:

动画

如果设置了多个 Action 无法在一行显示,则默认会进行折叠,通过点击 More 可以进行查看并操作:

image-20231210195547319

ActionManager.getInstance().getAction(actionName).actionPerformed(e) 可根据 Action 的 id 调用自行创建的或者平台已定义的 Action,例如打开设置界面,这里的 ShowSettings 就是已经定义好的:

image-20231211093004675

也就是说,只要我们知道内部定义的 Action 对应的 id,我们也就可以自由地使用平台已有的功能,同时如果有类似功能需求的时候还可以根据相应的类找到对应源码,然后我们再进行修改即可。哪有没有快速知道一个操作对应 Action 的方法呢?还好,平台内部也给我们准备了内部工具,打开和使用方式参考开启内部工具部分进行查看。

对话框通知

对话框通知的一般效果如下:

image-20231212150057692

官方推荐使用DialogWrapper抽象类来创建,上面效果对应的代码如下:

class DialogDemo(private val project: Project): DialogWrapper(project) {
    
    init {
        // 设置标题并初始化
        title = "Title"
        init()
    }

    // 创建布局面板
    override fun createCenterPanel(): JComponent {
        return panel {
            row {
                label("Hello, world!")
            }
        }
    }

}

其中 title 用于设置对话框左上角的标题,createCenterPanel()则用于创建布局内容,这里的布局对象同样支持我们在第二篇文章中的创建方式,也就是说除了基本的文本信息,各种表单输入框也都是支持的。而这里为了方便,就使用了Koltin UI DSL,也很好理解,可以参考官方文档进行学习,后续会再写一篇文章进行讲解,这里就不再展开说了。

当然除了以上基本的对话框内容,DialogWrapper还支持自定义按钮事件,并且可以显示帮助按钮以及不再展示的提示:

动画

上述效果对应的代码如下:

class DialogDemo(private val project: Project): DialogWrapper(project) {
    
    private lateinit var checkBox: Cell<JBCheckBox>
    
    init {
        // 设置标题并初始化
        title = "Title"
        init()
    }

    // 创建 Don't show again 选择框
    override fun createDoNotAskCheckbox(): JComponent {
        return panel { 
            row { 
                checkBox = checkBox("Do not show again")
            }
        }
    }

    // 设置帮助按钮 id
    override fun getHelpId(): String {
        return "ListenerHelp"
    }

    // 设置帮助按钮的 Tooltip
    override fun setHelpTooltip(helpButton: JButton) {
        helpButton.toolTipText = "Tip"
    }

    // 处理帮助按钮事件
    override fun doHelpAction() {
        showInfo("Help")
    }

    // 处理 OK 按钮事件
    override fun doOKAction() {
        super.doOKAction()
        showInfo("OK, value: ${checkBox.selected()}")
    }

    // 处理取消按钮事件
    override fun doCancelAction() {
        super.doCancelAction()
        showInfo("Cancel")
    }

    // 展示消息
    private fun showInfo(msg: String) {
        Notifications.Bus.notify(Notification("listener", msg, NotificationType.INFORMATION), project)
    }

    // 创建布局面板
    override fun createCenterPanel(): JComponent {
        return panel {
            row {
                label("Hello, world!")
            }
        }
    }

}

如果还有其它定制化需求,重写DialogWrapper内的相应方法即可。如果我们只是为了显示简单的文本信息,类似下述效果:

image-20231218123328330

也可以选择平台已经封装好的com.intellij.openapi.ui.Messages类,使用其中的工具方法即可,上述效果只需要一行代码即可:

Messages.showInfoMessage("Hello, world!", "Title")

Messages类中已经封装好了很多基础对话框,这里就不再一一展示。

编辑器提示

正如标题,这种信息的展示方式是在编辑器中通过悬浮框展示的,类似下述效果:

image-20231218102537826

使用方式也很简单,只需要一行代码即可:

e.getData(PlatformDataKeys.EDITOR)?.let { 
    HintManager.getInstance().showInformationHint(it, "Information")
}

同时除了showInformationHint还支持showErrorHint展示错误提示信息:

image-20231218102825834

不过还有一个showQuestionHint使用较为复杂,在 IDEA 中提示我们导包就是通过这种方式:

image-20231218103235567

这种通知方式除了包含提示外还会对指定单词增加下划线,这里就不复现完整的效果了,只展示下基础的使用方式:

e.getData(PlatformDataKeys.EDITOR)?.let {
    HintManager.getInstance().showQuestionHint(
        it, "Question", it.caretModel.offset, it.caretModel.offset + 6) {
        true
    }
}

效果如下:

image-20231218103521566

编辑器横幅

当我们新建一个 Java 项目缺没有配置 JDK,通常会在编辑器内出现类似下图的提示:

image-20231218124413132

这个提示配置 JDK 的提示就是编辑器横幅的效果。

实现这个效果也很简单,只需要继承EditorNotificationProvider并重写其中的方法即可:

class EditorBanner: EditorNotificationProvider {
    
    override fun collectNotificationData(project: Project, virtualFile: VirtualFile): Function<in FileEditor, out JComponent?> {
        return Function {
            val banner = EditorNotificationPanel()
            banner.text = "EditorBanner"
            banner.toolTipText = "ShowSettings"
            banner.createActionLabel("ShowSettings") {
                ShowSettingsUtil.getInstance().showSettingsDialog(project, "Editor")
            }
            banner
        }
        
    }

}

然后在plugin.xml中增加如下配置:

<extensions defaultExtensionNs="com.intellij">
	<editorNotificationProvider implementation="cn.butterfly.listener.ui.EditorBanner"/>
</extensions>

最终效果如下:

动画

这里展示编辑器横幅一般是先获取本地时候是否缺少指定配置,如果判断已经包含某个配置,则 collectNotificationData 需要返回 null,否则才需要创建 banner 进行展示。

“Got It” 提示

在更新 IDEA 后我们通常能看到类似下图中介绍新功能的提示:

img

这种效果也很容易实现,只需要使用GotItTooltip这个类即可,在这里我们结合前文对话框通知中创建的帮助按钮来使用(只展示变更部分):

private lateinit var helpButton: JButton

// 设置帮助按钮的 Tooltip
override fun setHelpTooltip(helpButton: JButton) {
    helpButton.toolTipText = "Tip"
    this.helpButton = helpButton
}

// 处理帮助按钮事件
override fun doHelpAction() {
    showInfo("Help")
    GotItTooltip("listener.tip.id", "GotItTooltip")
        .show(helpButton, GotItTooltip.BOTTOM_MIDDLE)
}

可以得到如下效果(点击帮助按钮后出现):

image-20231218145718929

GotItTooltip 使用了建造者模式,因此配置项可通过链式调用,可自行选择配置项:

image-20231218150150400

弹出框

除了以上常见的消息展示方式外,还有一种弹出框选项的方式,当我们使用Ctrl + Shift + Alt + /快捷键的时候会出现以下弹出框:

image-20231218154906724

这种弹出框则需要使用JBPopupFactory进行创建,使用方式也很简单:

class PopupAction:AnAction() {
    
    override fun actionPerformed(e: AnActionEvent) {
        JBPopupFactory.getInstance()
            .createListPopup(MyListPopupStep("Title", arrayOf("Option 1", "Option 2", "Option 3")))
            .showInFocusCenter()
    }
    
}

class MyListPopupStep constructor(title: String?, values: Array<String?>) : BaseListPopupStep<String>(title, *values) {
    
    override fun onChosen(selectedValue: String, finalChoice: Boolean): PopupStep<*> {
        // 选中事件
        return FINAL_CHOICE
    }

    override fun getIconFor(value: String): Icon? {
        // 设置图标
        return null
    }

    override fun isMnemonicsNavigationEnabled(): Boolean = false
    
}

以上代码可以实现如下的效果:

image-20231218155019025

除了使用createListPopup,还可以直接选择使用createMessage直接展示信息,或者使用createConfirmation展示只有 yes 和 no 的弹出框:

image-20231218155156931

使用createMessage("Hello, world!")的效果:

image-20231218155349883

使用createConfirmation("Title", {}, 0)的效果:

image-20231218155431263

事件监听

在开发插件的过程中,我们有时候需要在项目打开或关闭的时候去完成一些操作,而 IntelliJ 平台也为我们提供了相应的监听器类,当然除了项目监听,平台包含插件还自带了很多其它类型的监听器,这里就不逐一介绍了,以项目监听为例,然后再说明如何自定义一个事件监听器。

项目监听器

对项目打开和关闭的监听方式很简单,只需要继承ProjectManagerListener类即可,然后既可以在相应方法中处理对应操作:

class MyProjectManagerListener: ProjectManagerListener {
    
    init {
        // 项目启动处理
        val notification = Notification("listener", "Title", "Open", NotificationType.INFORMATION)
        Notifications.Bus.notify(notification, null)
    }

    override fun projectClosing(project: Project) {
        // 项目即将关闭
        println("Closing")
    }
    
}

同时在plugin.xml中增加如下配置:

<projectListeners>
	<listener class="cn.butterfly.listener.listener.MyProjectManagerListener"
                  topic="com.intellij.openapi.project.ProjectManagerListener"/>
</projectListeners>

在新版本中平台建议我们继承ProjectActivity类并通过postStartupActivity扩展点来实现,这里也展示一下使用方法:

class MyProjectActivity: ProjectActivity {
    
    override suspend fun execute(project: Project) {
        val notification = Notification("listener", "Title", "Open", NotificationType.INFORMATION)
        Notifications.Bus.notify(notification, null)
    }
    
}

然后也需要在plugin.xml中增加配置:

<extensions defaultExtensionNs="com.intellij">
	<postStartupActivity implementation="cn.butterfly.listener.listener.MyProjectActivity"/>
</extensions>

自定义事件监听器

在 IntelliJ 平台中的事件可以看作是发布订阅的模式,我们需要先定义一个主题,然后发布者发布相应的事件,订阅者实现对应的监听器事件即可。继续使用前文中的帮助按钮的例子,如果点击了帮助按钮,我们就在界面中显示帮助按钮被点击,下面介绍如何实现这个功能。

首先自定义一个监听器:

interface MyListener {
    
    companion object {
        var MY_TOPIC: Topic<MyListener> = Topic.create("listener", MyListener::class.java)
    
    }
    
    fun afterHelpBtnClicked(msg: String)

}

然后定义实现类:

class MyListenerA: MyListener {

    override fun afterHelpBtnClicked(msg: String) {
        Messages.showInfoMessage(msg, "Title")
    }
    
}

之后在plugin.xml中注册:

<projectListeners>
	<listener class="cn.butterfly.listener.listener.MyListenerA"
              topic="cn.butterfly.listener.listener.MyListener"/>
</projectListeners>

最后在点击了帮助按钮后发布事件用于测试:

// 处理帮助按钮事件
override fun doHelpAction() {
    showInfo("Help")
    // 发布事件
    project.messageBus.syncPublisher(MyListener.MY_TOPIC).afterHelpBtnClicked("点击了帮助按钮")
    GotItTooltip("listener.tip.id", "GotItTooltip")
        .show(helpButton, GotItTooltip.BOTTOM_MIDDLE)
}

效果如下:

动画

其它平台自带监听器

除了上文提到的项目监听器,平台还提供了很多其它监听器(还可以使用其它插件内定义的监听器,比如 Git 插件的一些事件),参考上述自定义监听器的实现和使用方式即可,完整列表可参考官方文档。

开启内部工具

选择Help | Edit Custom Properties...,然后在文件内加上idea.is.internal=true的配置,最后保存文件并重启 IDEA 即可。

重启后我们就可以使用了,例如上文提到的查看某个操作对应 Action 的 id,参考下述动图步骤即可,可以看到在打开设置界面后里面出现了我们前文使用的ShowSettings这个 id:

动画

内部工具的使用先到此为止,由于其中实用功能挺多的,就在后续专门用一篇文章进行讲解。

总结

本文讲解了关于消息通知和事件监听相关的内容,在下一篇文章将会实战开发一个类似 VSCode 中 Timer Master 的插件(如下图所示)来对这一部分的内容做一个小结。

image-20231210162740533

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1322971.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python Pandas Excel/csv文件的保存与读取(第14讲)

Python Pandas Excel/csv文件的读取于保存(第14讲)         🍹博主 侯小啾 感谢您的支持与信赖。☀️ 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔…

1.【分布式】分布式事务详解

分布式事务 1.分布式事务是什么&#xff1f;数据库事务 2.分布式事务产生的原因&#xff1f;存储层拆分服务层拆分 3.分布式事务解决方案4.分布式事务有哪些开源组件SeateTCC 分布式服务组件基于消息补偿的最终一致性 5.两阶段提交&#xff0c;三阶段协议详解二阶段提交协议三阶…

Excel怎样统计一列中不同的数据分别有多少个?

文章目录 1.打开Excel数据表2.选择“插入”&#xff0c;“数据透视表”3.选择数据透视表放置位置4.将统计列分别拖到“行”和“数值”区间5.统计出一列中不同的数据分别有多少个 1.打开Excel数据表 2.选择“插入”&#xff0c;“数据透视表” 3.选择数据透视表放置位置 4.将统计…

设计模式(三)-结构型模式(5)-外观模式

一、为何需要外观模式&#xff08;Facade&#xff09;? 要实现一个大功能&#xff0c;我们需要将它拆分成多个子系统。然后每个子系统所实现的功能&#xff0c;就由一个称为外观的高层功能模块来调用。这种设计方式就称为外观模式。该模式在开发时常常被使用过&#xff0c;所…

每日一练 | 华为认证真题练习Day150

1、IEEE802.1Q定义的VLAN帧格式总长度为多少字节&#xff1f; A. 4 B. 2 C. 3 D. 1 2、运行STP的交换机会发送BPDU。下面关于BPDU的说法正确的是&#xff08;&#xff09;&#xff08;多选&#xff09; A. BPDU帧的Control字段值为3 B. BFDU使用的是IEEE 802.3标准的帧 …

<软考>软件设计师-5计算机网络(总结)

1 网络功能和分类 1-1计算机网络的功能 计算机网络是计算机技术与通信技术相结合的产物&#xff0c;它实现了远程通信、远程信息处理和资源共享。计算机网络的功能:数据通信、资源共享、负载均衡、高可靠性。 1-2计算机网络按分布范围划分 1-3网络的拓扑结构 总线型&#xff0…

Linux常用基本命令操作

目录 一、认识shell 1、什么是shell 2、命令的本质 3、内部命令和外部命令 4、harsh缓存 5、命令执行的过程 6、如果打了一个命令&#xff0c;提示该命令不存在 7、命令提示符 8、Linux系统文件夹 二、Linux常用命令 1、通用Linux命令行格式 2、编辑Linux命令行的辅…

代码随想录第三十六天(一刷C语言)|背包问题理论基础分割等和子集

创作目的&#xff1a;为了方便自己后续复习重点&#xff0c;以及养成写博客的习惯。 一、背包问题 题目&#xff1a;有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品只能用一次&#xff0c;求解将哪些物品装…

PostgreSQL入门指南:快速学会创建和管理数据库!

当谈到数据库管理系统时&#xff0c;PostgreSQL是一个功能强大且广泛使用的开源关系型数据库。在本次讲解中&#xff0c;我将为您介绍如何创建和管理数据库&#xff0c;并提供一些有关PostgreSQL的基本概念和最佳实践的指导。 创建数据库 在开始之前&#xff0c;请确保您已经成…

Mac M系列安装配置VSCode

一、终端输入 安装command line tools xcode-select --install 这里是已经下载了 如果没有下载点击安装&#xff0c;等待安装完成即可 检验是否安装成功&#xff0c;终端输入 clang 如图所示是代表之前的command line tools安装 是安装成功的&#xff08;Clang会不断更新…

设计模式——代理模式(结构型)

引言 代理模式是一种结构型设计模式&#xff0c; 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问&#xff0c; 并允许在将请求提交给对象前后进行一些处理。 问题 为什么要控制对于某个对象的访问呢&#xff1f; 举个例子&#xff1a; 有这样一个消耗大量…

10.鸿蒙应用程序app创建第一个程序Helloworld

鸿蒙应用程序开发app_hap开发环境搭建 1.打开DevEco 2.创建项目 3.选择Empty Ability 4. 选择API6,支持java开发 5.点击Finish 6.启动本地模拟器参考方法 7.启动成功 8.运行程序 9.运行成功 其它文章点击专栏

spring之面向切面:AOP(2)

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…

Python Opencv实践 - 手部跟踪

使用mediapipe库做手部的实时跟踪&#xff0c;关于mediapipe的介绍,请自行百度。 mediapipe做手部检测的资料&#xff0c;可以参考这里&#xff1a; MediaPipe Hands: On-device Real-time Hand Tracking 论文阅读笔记 - 知乎论文地址&#xff1a; https://arxiv.org/abs/2006…

不用再找了,这是大模型实践最全的总结

随着ChatGPT的迅速出圈&#xff0c;加速了大模型时代的变革。对于以Transformer、MOE结构为代表的大模型来说&#xff0c;传统的单机单卡训练模式肯定不能满足上千&#xff08;万&#xff09;亿级参数的模型训练&#xff0c;这时候我们就需要解决内存墙和通信墙等一系列问题&am…

ATKXCOM串口助手接受中文字符乱码问题

中文乱码大多是编码格式问题&#xff0c;如心知天气API返回的数据编码格式为UTF-8格式&#xff0c;同理串口调试助手需要更改为对应的编码格式&#xff0c;正点原子的串口调试助手具有多种编码格式可以更改。 PS:点击左下角设置图标即可设置

手撕分布式缓存---HTTP Client搭建

经过上个章节的学习&#xff0c;我们已经实现了一致性哈希算法&#xff0c;这个算法保证我们可以在节点发生变动时&#xff0c;最少的key请求受到影响&#xff0c;并返回这个节点的名称&#xff1b;这很大程度上避免了哈希雪崩和哈希穿透的问题。这个章节我们要基于此实现完整的…

tensorflow入门

一、怎样入手TensorFlow TensorFlow是一个用于机器学习和深度学习的开源框架&#xff0c;它提供了一种灵活的方式来构建和训练神经网络模型。以下是一些TensorFlow框架入门的建议&#xff1a; 学习Python语言&#xff1a;TensorFlow主要使用Python语言进行开发&#xff0c;因此…

自动驾驶技术入门平台分享:百度Apollo开放平台9.0全方位升级

目录 平台全方位的升级 全新的架构 工具服务 应用软件&#xff08;场景应用&#xff09; 软件核心 硬件设备 更强的算法能力 9.0版本算法升级总结 更易用的工程框架 Apollo开放平台9.0版本的技术升级为开发者提供了许多显著的好处&#xff0c;特别是对于深度开发需求…

【Java】工业园区高精准UWB定位系统源码

UWB (ULTRA WIDE BAND, UWB) 技术是一种无线载波通讯技术&#xff0c;它不采用正弦载波&#xff0c;而是利用纳秒级的非正弦波窄脉冲传输数据&#xff0c;因此其所占的频谱范围很宽。UWB定位系统依托在移动通信&#xff0c;雷达&#xff0c;微波电路&#xff0c;云计算与大数据…