Custom settings view
When you need custom UX such as a "Get a key" button, "Test connection", dual-channel switching or dynamic lists,
config_schemais not enough — ship your own QML settings view.
Declaration
json
{
"has_settings_tab": true,
"tab_id": "my_plugin",
"tab_title": "我的插件",
"ui_extensions": { "settings_view": "CustomSettingsView.qml" }
}has_settings_tab: true→ a dedicated Tab appears in the main settings window.tab_idmust be unique;tab_titleis the Tab title (put it inlocales/to get automatic translation, see i18n).settings_viewdoes not take part in UI slot mutual exclusion — every plugin has its own form.
Injected properties
A component is only assigned a property if it declares that property:
| Property | Description |
|---|---|
pluginId | Your plugin id |
bridge | Host bridge (public slots, aligned with Host SPI) |
rootWin | Host settings window instance (use it when you need to attach a dialog / position something) |
pluginConfig | Current config dictionary (snapshot) |
qml
import QtQuick 2.15
Item {
id: root
property string pluginId: ""
property var bridge: null
property var rootWin: null
property var pluginConfig: ({})
// Live config refresh: getPluginConfig is a transient call, you must listen to the signal
property var cfg: (typeof bridge !== "undefined" && bridge && bridge.getPluginConfig)
? bridge.getPluginConfig(root.pluginId) : ({})
Connections {
target: (typeof bridge !== "undefined") ? bridge : null
function onPluginConfigChanged(pid) {
if (pid === root.pluginId) {
root.cfg = bridge.getPluginConfig(root.pluginId) || ({})
}
}
}
// Write: persist + hot-sync the plugin's update_config
function setVal(key, value) {
if (typeof bridge !== "undefined" && bridge && bridge.setPluginConfigValue)
bridge.setPluginConfigValue(root.pluginId, key, value)
}
}How to build common custom UX
| Need | How |
|---|---|
| "Get a key" button | Button → Qt.openUrlExternally("https://…"); next to it, spell out which fields to copy |
| "Test connection" | Button → bridge.invokePluginActionAsync(pluginId, "test_connection", {}); the plugin makes the request in the background and returns the result via emit_event("test_result", {...}) |
| Pick a file / pick a folder | bridge.pickFile(title, filter) / bridge.pickFolder() (equivalent to host.pick_file / host.pick_folder) |
| Show a dynamic list (voices / models / devices) | Prefer the host's existing components, or push data through pluginEvent; do not fetch HTTP yourself |
| Long task progress | The bridge.beginProgress family, or have the plugin report status via emit_event |
Plugin-side action entry points are always
action_<name>(see tool and hotkeys). From QML usebridge.invokePluginAction(pluginId, name, params)(synchronous, instantaneous actions only) orbridge.invokePluginActionAsync(...)(recommended).
Visual rules
- Use
ThemeTokensandPluggableWindowBackgroundto adapt to the theme automatically. - Never use Emoji / characters as icons; vector SVG is mandatory.
- Prefer reusing the host's component style for buttons and inputs instead of inventing your own (it falls apart when the theme changes).
- User-facing strings go through
I18n.t(...)and into the plugin's ownlocales/.
Division of labour with config_schema
config_schema | settings_view | |
|---|---|---|
| Applies to | Ordinary fields (string / number / switch / dropdown) | Custom interactions, dynamic data, external links |
| Rendering | Automatic, by the host | Written by you |
| Saving | Automatic persistence + hot sync | You call bridge.setPluginConfigValue / savePluginConfig |
| Coexistence | ✅ Yes: the settings Tab renders config_schema first, then mounts the extra blocks of settings_view |
Recommendation: put everything you can express with config_schema into config_schema (you get storage, hot refresh and i18n fallbacks for free), and write only the genuinely custom parts into settings_view.
Reference implementations
extensions/community_cloud_dashscope_asr/SettingsView.qml— "Get a key" + dual channelextensions/community_cloud_xunfei_si/SettingsView.qml— multi-field credential formextensions/community_ai_interview_copilot/— the full settings Tab of an assistant
