UI 扩展(QML)
用 QML 替换宿主的界面组件(底板、音柱、字幕视图、装饰…)。 槽位清单与背景绘制准则见 UI 插槽——本页只讲怎么写。
声明
type 用 theme(局部插槽扩展建议 style_layer: "effects"):
json
{
"id": "community.theme.neon_wave",
"name": "极光霓虹律动波形条",
"version": "1.0.0",
"target_app_version": "0.2",
"type": "theme",
"style_layer": "effects",
"ui_extensions": {
"audio_level": "CustomWaveform.qml"
},
"enabled": false
}只使用宿主已消费的槽位;overlay_root / style_bar 当前无 Loader,勿用。
注入属性
| 属性 | 适用 | 说明 |
|---|---|---|
hostContext | 悬浮窗类 | style / subtitle / audio / window / invoke |
bridge | 全部 | 公开槽(与 Host SPI 对齐),如 bridge.getPluginConfig(pluginId) |
pluginId | 全部 | 你的插件 id |
appState | 背景组件 | 全局应用状态(bgColor / bgOpacity / neonBorderColor…) |
window / targetWindow | 背景组件 | 目标窗体实例指针 |
customRadius | 背景组件 | 宿主注入的目标窗口圆角(设置中心 16、历史窗 16、通用弹窗 12…) |
hovered | 悬浮窗 | 是否处于鼠标悬停态 |
组件声明了对应 property 才会被赋值——不需要的就别声明。
完整属性与 HostContext 见 QML 界面插槽与 HostContext。
配置读取:必须有 Connections
⚠️
bridge.getPluginConfig(pluginId)是瞬态函数调用,不是 QML 响应式属性。 只读一次的话,用户在设置面板改开关时你的组件不会更新。
qml
import QtQuick 2.15
Item {
id: root
anchors.fill: parent
readonly property string myPluginId: "community.theme.my_theme"
// 1. 初始读取
property var cfg: (typeof bridge !== "undefined" && bridge && bridge.getPluginConfig)
? bridge.getPluginConfig(root.myPluginId) : ({})
// 2. 解包 + 默认值
readonly property bool enableGlow: (cfg && typeof cfg.neon_glow !== "undefined") ? cfg.neon_glow : true
// 3. 必须监听信号,热刷新 cfg
Connections {
target: (typeof bridge !== "undefined") ? bridge : null
function onPluginConfigChanged(pid) {
if (pid === root.myPluginId) {
root.cfg = bridge.getPluginConfig(root.myPluginId) || ({})
}
}
}
// 4. 绑定到视觉图层
Rectangle {
anchors.fill: parent
visible: root.enableGlow
border.color: "#00f0ff"
border.width: 1.5
}
}视觉规范(硬要求)
- 所有插件 QML 必须通过
ThemeTokens与PluggableWindowBackground自动适配主题切换。 - 禁止用 Emoji 或文本字符充当图标,必须使用矢量 SVG(
ThemeTokens.icon或插件自带 SVG)。 - 组件里不要写死字体族/图标色——写死会在用户换主题时穿帮;用
ThemeTokens.*。 - 背景类插槽必须自绘边框与圆角自适应,详见 UI 插槽 § 背景插槽准则。
调用插件的 Python 动作
QML 侧通过槽位调插件的 action_*:
qml
// 同步(会阻塞 UI 线程,只适合瞬时动作)
bridge.invokePluginAction(pluginId, "run", {"source": "ui"})
// 耗时动作请用异步版,或让 Python 侧走 self.run_in_background()
bridge.invokePluginActionAsync(pluginId, "ask", {"prompt": "…"})插件发上来的事件在 QML 里由 pluginEvent(plugin_id, event_name, payload) 接收, 见 事件总线。
热调试
改 QML 后:停用再启用插件即可重载;或重启应用(开发期可用 TRANSBOX_QML_RELOAD=1 的宿主热重载能力,见 热调试)。
