QML UI Slots & HostContext
Map a slot name to a .qml file inside your package via ui_extensions in mod.json to replace the corresponding visual region without modifying the host application.

1. Consumed Slots (safe to use)
| Slot | Replaces | Purpose |
|---|---|---|
overlay_background | Subtitle overlay backplate | Frosted glass, gradients, noise |
caption_view | Subtitle typesetting area | Custom bilingual layouts |
audio_level | Bottom-right volume / level meter | Waveforms, spectrums |
alert_capsule | Missing key / model-not-ready alert capsule | Custom alert UI |
overlay_decorations | Overlay top-layer decorations | HUD cut corners, particles |
style_bar_actions | Control-bar action extension area | Extra buttons |
settings_background | Settings center backplate | Match the theme |
history_background | History window backplate | — |
dialog_background | Dialog / modal backplate | — |
window_background | Multi-window cascade fallback layer | Settings/history fallback |
Currently unconsumed (registered but no Loader — do not rely on them):overlay_root、style_bar.
Non-standard key settings_view:
Does not go through the slot chain above; read by the settings-page plugin container for full engine/suite configuration UIs.
2. Cascade Fallback
Multi-window backgrounds try in this order:
- Dialogs:
dialog_background→window_background→overlay_background - Settings/history:
window_background→overlay_background
Therefore, if you only write overlay_background, other windows may still reuse your backplate style.
3. window_backdrop
Declared in theme.json (not mod.json):
| Value | Meaning |
|---|---|
acrylic | Windows acrylic |
mica / mica_alt | Mica (Win11) |
blur | Blur |
none | No system material |
You can also call hostContext.window.setBackdrop("acrylic") from QML.
4. HostContext (overlay-class slots)
The host injects property var hostContext into the component root object:
import QtQuick 2.15
Item {
id: root
property var hostContext: null
readonly property color textColor:
hostContext ? hostContext.style.textColor : "#ffffff"
readonly property string translation:
hostContext ? hostContext.subtitle.currTranslation : ""
readonly property bool hovered:
hostContext ? hostContext.window.hovered : false
function callPython() {
if (!hostContext) return
hostContext.invoke("my.plugin.id", "ping", { "n": 1 })
}
Rectangle {
anchors.fill: parent
color: hostContext ? hostContext.style.bgColor : "#00000000"
opacity: hostContext ? hostContext.style.bgOpacity : 1.0
Text {
anchors.centerIn: parent
text: translation
color: textColor
font.pixelSize: hostContext ? hostContext.style.fontSize : 24
font.family: hostContext ? hostContext.style.fontFamily : "sans-serif"
}
}
}Subsystems
styletextColor · bgColor · fontOpacity · bgOpacity · fontSize · fontFamily · themeId · primary · primaryHover · primaryGlow · radiusLarge
subtitlecurrSource · currTranslation · hasRealSubtitle · prevSource · prevTranslation · hasPrev · isTyping · isSourceActive · isTranslationActive
audioisMicMuted · audioDuckingEnabled · playbackDevice
windowhovered · dragging · width · height · setBackdrop(type) · setClickThrough(bool)
RPChostContext.invoke(pluginId, action, params) → Python handle_action → action_<action>(**params)
5. Extra Injected Properties (by slot)
| Slot | Extra properties / signals |
|---|---|
overlay_background, etc. | hovered, appState |
caption_view | appState, bridge |
alert_capsule | alertType, alertCategory, showCapsule; signals closeRequested, gotoSettingsRequested, switchLocalRequested |
style_bar_actions | hostContext |
| Window backplates | window, appState; may use customRadius |
Multi-window backplates may declare:
property bool enforceCustomRadius: true
property real customRadius: 16
property real customOpacity: 0.856. settings_view (plugin settings page)
mod.json:
{
"ui_extensions": {
"settings_view": "CustomSettingsView.qml"
},
"has_settings_tab": true,
"tab_id": "my_plugin",
"tab_title": "我的插件"
}The component root object may declare (the host assigns them as needed):
property string pluginId: ""
property var bridge: null
property var rootWin: null
property var pluginConfig: ({})Save example:
if (bridge) {
bridge.setPluginConfigValue(pluginId, "gain_db", 12.5)
}If you only need a form: you do not need settings_view — use config_schema instead.
7. Hands-on: Replace the Volume Bar
mod.json fragment:
{
"type": "theme",
"ui_extensions": { "audio_level": "CustomWaveform.qml" }
}import QtQuick 2.15
Item {
id: root
anchors.fill: parent
property real level: 0.0
property var hostContext: null
// If the host does not provide a level signal, you can wire Connections to bridge yourself (version-dependent)
Row {
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.margins: 8
spacing: 2
opacity: root.level > 0.01 ? 0.95 : 0.0
Repeater {
model: 5
Rectangle {
width: 2
height: Math.max(2, Math.min(10, root.level * 10))
color: "#00f0ff"
}
}
}
}Complete runnable references: extensions/example_custom_waveform_ui/、example_theme_cyberpunk/.
8. Design & Compatibility Tips
- Keep
anchors.fill: parenton the component root so you do not break the host layout. - Null-check every
hostContextaccess so the component previews safely. - Do not depend on global object names that do not appear in this document.
- Only one extension is enabled per slot at a time; enabling automatically applies mutual exclusion.