gogpu/ui Plugin System 深度解析
gogpu/ui 的 Plugin System 是其架构中极具特色的设计,提供了完整的插件生命周期管理、依赖解析和资源加载机制。
核心架构
┌─────────────────────────────────────────────────────────────┐
│ PLUGIN SYSTEM │
├─────────────────────────────────────────────────────────────┤
│ Plugin Interface │
│ ├── Name() string │
│ ├── Version() string │
│ ├── Dependencies() []Dependency │
│ ├── Init(ctx *PluginContext) error │
│ └── Shutdown() error │
├─────────────────────────────────────────────────────────────┤
│ PluginContext (初始化上下文) │
│ ├── Widgets *registry.WidgetRegistry // 组件注册 │
│ ├── Themes *theme.ThemeRegistry // 主题注册 │
│ ├── Layouts *layout.Registry // 布局注册 │
│ └── Assets AssetLoader // 资源加载 │
└─────────────────────────────────────────────────────────────┘
完整的 Plugin 实现示例
package myplugin
import (
"github.com/gogpu/ui/plugin"
"github.com/gogpu/ui/registry"
"github.com/gogpu/ui/theme"
)
type MyPlugin struct{}
func (p *MyPlugin) Name() string { return "my-awesome-plugin" }
func (p *MyPlugin) Version() string { return "1.0.0" }
// 声明依赖其他插件
func (p *MyPlugin) Dependencies() []plugin.Dependency {
return []plugin.Dependency{
{Name: "material3", Version: ">=0.20.0"},
{Name: "icons-pack", Version: ">=1.0.0,<2.0.0"},
}
}
// 初始化时注册组件、主题和资源
func (p *MyPlugin) Init(ctx *plugin.PluginContext) error {
// 1. 注册自定义组件
ctx.Widgets.Register("fancy-button", NewFancyButton, registry.WidgetInfo{
Name: "fancy-button",
Description: "A fancy animated button",
Category: registry.CategoryInput,
})
// 2. 注册主题
ctx.Themes.Register("fancy-theme", fancyTheme, theme.ThemeInfo{
Name: "Fancy Theme",
Description: "Custom theme with animations",
})
// 3. 注册布局
ctx.Layouts.Register(&MasonryLayout{})
// 4. 加载资源
ctx.Assets.LoadFont("fancy-font", fontData)
ctx.Assets.LoadIcon("sparkles", iconData)
ctx.Assets.LoadImage("background", bgImageData)
return nil
}
func (p *MyPlugin) Shutdown() error {
// 清理资源
return nil
}
// 在 init() 中自动注册
func init() {
plugin.MustRegister(&MyPlugin{}, plugin.PluginInfo{
Name: "my-awesome-plugin",
Description: "My custom UI plugin with fancy components",
Version: "1.0.0",
Author: "Your Name",
License: "MIT",
Homepage: "https://github.com/yourname/my-ui-plugin",
})
}
版本约束支持
Plugin System 支持语义化版本约束:
// 精确版本
{Name: "base-plugin", Version: "1.0.0"}
// 最小版本
{Name: "material3", Version: ">=0.20.0"}
// 版本范围
{Name: "icons-pack", Version: ">=1.0.0,<2.0.0"}
// 小于某版本
{Name: "legacy-compat", Version: "<2.0.0"}
应用程序使用插件
package main
import (
"log"
"github.com/gogpu/gogpu"
"github.com/gogpu/ui/app"
"github.com/gogpu/ui/plugin"
"github.com/gogpu/ui/registry"
// 导入插件包会自动注册
_ "github.com/yourname/my-ui-plugin"
_ "github.com/gogpu/ui/theme/material3"
)
func main() {
// 初始化所有已注册的插件
if err := plugin.Initialize(); err != nil {
log.Fatal(err)
}
defer plugin.Shutdown()
// 查看已加载的插件
for _, name := range plugin.List() {
info, _ := plugin.Info(name)
fmt.Printf("✓ %s v%s - %s
", name, info.Version, info.Description)
}
// 查看加载顺序
fmt.Println("
Load order:", plugin.LoadOrder())
// 从插件创建组件
gogpuApp := gogpu.NewApp(gogpu.DefaultConfig())
uiApp := app.New(
app.WithWindowProvider(gogpuApp),
)
// 使用插件注册的组件
fancyButton, _ := registry.CreateWidget("fancy-button", nil)
uiApp.SetRoot(fancyButton)
gogpuApp.Run()
}
PluginManager 生命周期
Register (init阶段)
↓
Initialize (main中调用)
├── 验证所有依赖已注册
├── 检查版本约束
├── 拓扑排序解析加载顺序
└── 按顺序调用每个 Plugin.Init()
↓
应用运行期间
↓
Shutdown (defer调用)
└── 逆序调用每个 Plugin.Shutdown()
错误处理
Plugin System 提供了详细的错误类型:
| 错误 | 说明 |
|---|
ErrCircularDependency | 检测到循环依赖 |
ErrDependencyNotFound | 依赖的插件未注册 |
ErrVersionMismatch | 版本约束不满足 |
ErrAlreadyInitialized | 插件已初始化 |
ErrPluginNotFound | 插件未找到 |
高级用法:自定义 AssetLoader
// 实现自定义资源加载器 (如从网络加载)
type NetworkAssetLoader struct {
baseURL string
cache map[string][]byte
}
func (n *NetworkAssetLoader) LoadFont(name string, data []byte) error {
// 从网络加载字体
resp, err := http.Get(n.baseURL + "/fonts/" + name)
// ...
}
// 使用自定义 Context
ctx := plugin.NewPluginContext(
registry.Global(),
theme.Global(),
layout.Global(),
&NetworkAssetLoader{baseURL: "https://cdn.example.com"},
)
plugin.InitializeWithContext(ctx)
测试中的 Plugin System
func TestMyPlugin(t *testing.T) {
// 创建独立的 PluginManager
pm := plugin.NewPluginManager()
// 注册测试插件
pm.Register(&MyPlugin{}, plugin.PluginInfo{
Name: "test-plugin",
})
// 初始化
if err := pm.Initialize(); err != nil {
t.Fatal(err)
}
// 测试...
// 清理
pm.Shutdown()
pm.Clear()
}
设计亮点
- 自动发现 - 通过
init() 函数自动注册,无需手动导入后注册 - 依赖解析 - 自动处理插件间的依赖关系和加载顺序
- 版本管理 - 完整的语义化版本约束支持
- 资源隔离 - 每个插件通过 PluginContext 访问注册表,避免冲突
- 线程安全 - 所有操作线程安全,支持并发场景
- 测试友好 - 支持创建独立的 PluginManager 实例进行测试
这个 Plugin System 的设计使得 gogpu/ui 具备了真正的可扩展性,开发者可以轻松分发和复用 UI 组件包。