UI、通知与用户体验¶
插件 UI 的目标不是“能显示控件”,而是让用户感觉它就是 IDE 的自然组成部分。官方文档建议优先使用 IntelliJ Platform 提供的 UI 组件、Kotlin UI DSL、DialogWrapper、非模态通知、Tool Window 和状态栏 Widget,而不是直接堆普通 Swing 控件。
UI 选择原则¶
| 用户意图 | 推荐入口 | 不推荐 |
|---|---|---|
| 执行一个明确命令 | Action、Popup、Toolbar | 自定义按钮散落在多个面板 |
| 长期查看插件状态 | Tool Window | 反复弹窗 |
| 配置插件行为 | Settings / Configurable | 自定义配置文件编辑器 |
| 阻止用户继续前需要输入 | DialogWrapper | 原生 JDialog |
| 告知不阻塞的结果或警告 | Notification、Editor Hint、Editor Banner | 模态 Message Box |
| 展示总是相关的小状态 | Status Bar Widget | 常驻 Tool Window 或通知 |
如果一个功能可以通过已有 IDE 模式表达,就不要发明新的交互模式。
使用平台 UI 组件¶
IntelliJ Platform 提供大量定制 Swing 组件。它们会自动适配主题、缩放、字体、间距和 IDE 行为。常见建议:
- Settings 和 Dialog 中使用 Kotlin UI DSL。
- 对旧 Swing 组件启用 Plugin DevKit 的
Undesirable class usage检查,寻找平台替代组件。 - 使用 UI Inspector 查看 IDE 现有 UI 的组件实现。
- UI 文案遵循 JetBrains UI Guidelines:短、明确、面向用户动作。
- Kotlin 项目不要使用 UI Designer 插件生成表单。
Kotlin UI DSL 适合设置页和对话框:
panel {
row("Endpoint:") {
textField()
.bindText(settings::endpoint)
.comment("Base URL used by the plugin.")
}
row {
checkBox("Enable analysis")
.bindSelected(settings::enabled)
}
}
DialogWrapper¶
平台模态对话框应继承 DialogWrapper。它提供:
- 平台一致的 OK/Cancel/Help 按钮顺序。
Esc关闭、按钮焦点切换等快捷键。- 对话框尺寸记忆。
- 非模态输入校验。
- “Do not ask again” 等标准行为。
最小结构:
class ExampleDialog(project: Project) : DialogWrapper(project) {
private val nameField = JBTextField()
init {
title = "Create Example"
init()
initValidation()
}
override fun createCenterPanel(): JComponent {
return panel {
row("Name:") {
cell(nameField)
.align(AlignX.FILL)
}
}
}
override fun getPreferredFocusedComponent(): JComponent = nameField
override fun doValidate(): ValidationInfo? {
return if (nameField.text.isBlank()) {
ValidationInfo("Name is required", nameField)
} else {
null
}
}
}
显示并判断结果:
if (ExampleDialog(project).showAndGet()) {
// OK pressed
}
不要在用户按 OK 后再弹一个错误框提示输入无效。应在 doValidate() 中返回 ValidationInfo,让错误直接贴近输入控件。
通知用户¶
官方文档明确建议避免用模态消息框通知错误或普通事件。根据场景选择非模态 UI:
| 场景 | 推荐 API |
|---|---|
| 编辑器中当前动作无法完成 | HintManager.showErrorHint() |
| 文件顶部需要用户配置 SDK、环境或依赖 | EditorNotificationProvider + EditorNotificationPanel |
| 重要新功能或变更提示 | GotItTooltip |
| 普通后台任务结果、警告、需要用户稍后处理 | Notification Balloons |
| 对话框输入错误 | DialogWrapper.doValidate() |
Editor Banner 示例思路:
<extensions defaultExtensionNs="com.intellij">
<editorNotificationProvider implementation="com.example.ExampleEditorNotificationProvider"/>
</extensions>
如果不访问索引,Provider 应实现 DumbAware,保证索引构建期间也能显示关键配置提示。
Notification Balloon¶
通知适合非阻塞消息。典型内容包括同步完成、配置缺失、后台任务失败、插件需要用户打开设置页等。
实践建议:
- 使用明确的 notification group。
- 提供可执行 action,例如 “Open Settings”。
- 错误消息要包含用户下一步,而不是只展示异常文本。
- 不要把大量日志塞进通知;详细信息应进入 Event Log、Run Console 或 Tool Window。
- 高频事件不要逐条发通知,应该合并或只显示状态。
Status Bar Widget¶
状态栏 Widget 适合展示总是相关、短小、当前上下文相关的信息,例如编码、分支、当前文件状态、插件开关。
注册入口是 StatusBarWidgetFactory:
<extensions defaultExtensionNs="com.intellij">
<statusBarWidgetFactory
id="ExampleStatus"
implementationClass="com.example.ExampleStatusBarWidgetFactory"/>
</extensions>
注意:
plugin.xml的id必须与StatusBarWidgetFactory.getId()匹配。- 只展示真正值得常驻的信息,状态栏空间非常有限。
- 文件相关状态可继承
StatusBarEditorBasedWidgetFactory。 - Widget 需要实现
StatusBarWidget,由 factory 创建和释放。 - LightEdit 模式默认不显示 Widget;需要支持时实现
LightEditCompatible。
UI 可访问性与可维护性¶
最低检查:
- 支持亮色、暗色和高对比主题。
- 文本不写死颜色,图标使用主题适配资源。
- 所有后台任务有进度和取消路径。
- 错误提示贴近用户上下文。
- 设置页支持搜索。
- 控件文案能被非插件作者理解。
- UI 创建和模型访问遵守 EDT/BGT 与读写动作规则。