Skip to content

Custom settings view ​

When you need custom UX such as a "Get a key" button, "Test connection", dual-channel switching or dynamic lists, config_schema is 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_id must be unique; tab_title is the Tab title (put it in locales/ to get automatic translation, see i18n).
  • settings_view does 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:

PropertyDescription
pluginIdYour plugin id
bridgeHost bridge (public slots, aligned with Host SPI)
rootWinHost settings window instance (use it when you need to attach a dialog / position something)
pluginConfigCurrent 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 ​

NeedHow
"Get a key" buttonButton → 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 folderbridge.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 progressThe 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 use bridge.invokePluginAction(pluginId, name, params) (synchronous, instantaneous actions only) or bridge.invokePluginActionAsync(...) (recommended).


Visual rules ​

  • Use ThemeTokens and PluggableWindowBackground to 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 own locales/.

Division of labour with config_schema ​

config_schemasettings_view
Applies toOrdinary fields (string / number / switch / dropdown)Custom interactions, dynamic data, external links
RenderingAutomatic, by the hostWritten by you
SavingAutomatic persistence + hot syncYou 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 channel
  • extensions/community_cloud_xunfei_si/SettingsView.qml — multi-field credential form
  • extensions/community_ai_interview_copilot/ — the full settings Tab of an assistant

TransBox — empowering borderless cross-language communication for everyone