The themed QML building blocks under qrc:/qml/Item/ that the built-in Spirit
System UI is made of β the same ones covered briefly in
Ready-made components, here with every property and
signal. Import a category the same way the project template does, e.g.
import "qrc:/qml/Item/Button", then use any component from it unqualified.
Two kinds of signal
Don't confuse these component signals with the root-level signalXxx(...) calls
in the API Reference. A root signal is declared on
Spirit UI itself and wired to the firmware β call it from anywhere, and where it
answers back, it does so through a matching root property or handler.
A component signal below (clicked, getValue, selectedId,
β¦) belongs to the individual Item you instantiate, exactly like any plain QML
signal. Spirit UI has no handler for these β there's nothing "returning" a
value from the root window to read. You attach your own onXxx: { ... } block right
where you place the component in your app.qml (or screen.qml), the same
way every built-in menu screen does. If your handler needs to read or persist something, that's
the point where you'd reach for a root signal β most often signalAppsCfgLoad /
signalAppsCfgSet (see Apps) to load or save
your own app's settings.
No components match your search.
CheckBox
import "qrc:/qml/Item/CheckBox" β a single themed toggle
switch.
CheckBox
A restyled Qt Quick Controls Switch β a sliding pill
indicator instead of the platform-default look, colored from theme_checkbox.
It adds no properties or signals of its own beyond what Switch already
provides, so everything below is inherited, not custom.
Property
Type
Description
checked
bool
Current on/off state. Bindable both ways β set it to pre-check the box, read it to react to changes.
position
real
0.0β1.0 slide progress; only ever rests at exactly 0.0 or 1.0 here (no drag-to-toggle track), so treat it as a second boolean.
pressed
bool
True while the thumb is being pressed.
enabled
bool
Inherited from Item; dims and disables interaction when false.
Signal
Handled by
Reading the value
onToggled()
You, in your own QML
Fires on a completed toggle. Read checked inside the handler.
onCheckedChanged()
You, in your own QML
Fires whenever checked changes, including programmatically β use this instead of onToggled if you also set checked from code.
onPositionChanged()
You, in your own QML
The pattern used throughout the built-in menus: guard on position == 0.0 || position == 1.0 to ignore in-flight animation frames, then act on the settled value β see the example below.
Real usage, from the built-in channel-reverse toggle:
import "qrc:/qml/Item/Icon" β tappable icon-plus-caption
tiles, in three sizes/styles used for menu grids, the app drawer, and side panels.
MenuIcon
200Γ160 icon with a caption below it β the size used by the on-device
app drawer and top-level menu grids.
Property
Type
Description
iconText
string
Caption shown under the icon, ellipsized if it overflows.
iconPath
string
Image source (loaded asynchronously).
enabled
bool
Inherited; dims the icon image to 50% opacity when false (it still catches taps unless you separately gate onClicked).
Signal
Handled by
Reading the value
signal action()
You, in your own QML
Fires on tap. No parameters β the tile itself carries no state, so just act directly in the handler (e.g. stack.push(...) or signalAppsExec(...)).
PanelIcon
125Γ100, icon only (no caption) β the compact size used in side
panels. Includes a small built-in press-scale animation.
Property
Type
Description
iconText
string
Declared for consistency with the other Icon components but not displayed by PanelIcon itself.
iconPath
string
Image source (loaded asynchronously).
Signal
Handled by
Reading the value
signal action()
You, in your own QML
Same as MenuIcon β no parameters, act directly in the handler.
MdlTypeIcon
160Γ160, rounded dark card with the caption overlaid on the image
instead of below it β used for model-type pickers.
Property
Type
Description
iconText
string
Caption drawn centered over the image.
iconPath
string
Image source.
Signal
Handled by
Reading the value
signal action()
You, in your own QML
Same pattern as the other two Icon components.
Button
import "qrc:/qml/Item/Button" β three themed buttons; the
template's first app sample uses GroupFrame +
plain Label, but any menu-style tap target should use one of these instead of a
bare Rectangle + MouseArea.
MenuButton
The rounded purple-gradient pill button used throughout menu screens β
the one Calling signals uses as its example.
Property
Type
Description
text
string
Label text, centered and word-wrapped.
pressed
bool
Read-only; true while held down (darkens the gradient).
enabled
bool
Inherited; grays the label and stops it reacting to taps.
Signal
Handled by
Reading the value
signal clicked()
You, in your own QML
No parameters. Handle it exactly like a built-in Button's onClicked.
DlgButton
A borderless text-only button, purple normally / black while
pressed β used for the Yes/No answers inside DlgWindow.
Rarely needed directly; use DlgWindow instead unless you're building a custom
dialog layout.
Property
Type
Description
text
string
Label text.
pressed
bool
Read-only press state.
Signal
Handled by
Reading the value
signal clicked()
You, in your own QML
No parameters.
FeatureButtonNote
A thin, unthemed wrapper around Qt Quick Controls' own
Button β fixed 60Γ90, checkable, icon-over-text layout. It doesn't reset the
platform style the way the other two components do, so it looks like a generic Qt button
rather than matching the radio's theme; check how/where it's used in the built-in UI before
reaching for it in your own app.
Property
Type
Description
checkable / checked
bool
Inherited from Button; stays down after a tap instead of springing back.
icon.name / icon.width / icon.height
β
Icon shown above the label text (display: Button.TextUnderIcon).
text
string
Inherited label text.
Signal
Handled by
Reading the value
onClicked() / onToggled()
You, in your own QML
Standard Qt Quick Controls Button signals β read checked inside the handler for the checkable state.
Adjuster
import "qrc:/qml/Item/Adjuster" β a pill showing a
numeric value that, when tapped, opens the built-in value-picker wheel to edit it. This is the
control behind almost every numeric setting in the built-in menus.
This one is different: two signals, no parameters, both fire on
every open
An Adjuster doesn't hold a value on its own β it's a thin editor around
whatever value you supply. Every time it's tapped open, it first emits
getValue() so you can (re)populate its value property from your own
data right before editing starts, then hands the wheel a callback that emits
setValue() once the pilot confirms a new number β at that point
value already holds the new number, so read it inside your
onSetValue handler and write it wherever it needs to persist.
Adjuster
Integer-valued variant.
Property
Type
Description
value
int
The current number. Set it in your onGetValue handler; read it in onSetValue.
range_min / range_max
int
Wheel bounds. Defaults 0β100.
step
int
Wheel increment. Default 1.
postfix
string
Unit suffix appended after the number, e.g. "%" or "GMT".
side
int
Passed straight through to the value-picker's own layout β leave at 0 unless you're matching a specific built-in picker's positioning.
live
bool
When true, setValue() fires continuously while the wheel spins, not just on confirm β use for settings that should preview live (e.g. a volume level).
Signal
Handled by
Reading the value
signal getValue()
You, in your own QML
Set value from your own data here, right before the wheel opens.
signal setValue()
You, in your own QML
Read value here (already updated) and persist it β for your own app's settings, typically via signalAppsCfgSet.
Same interface as Adjuster, for fractional values.
Property
Type
Description
value / range_min / range_max / step
double
Same roles as Adjuster, as floating-point.
decimals
int
Digits shown after the decimal point. Default 1.
postfix / side / live
β
Same as Adjuster.
Signal
Handled by
Reading the value
signal getValue() / signal setValue()
You, in your own QML
Identical pattern to Adjuster above.
AdjusterStabi and AdjusterFloatStabi are
flight-mode-aware variants used only inside the Spirit Controller menus: a long-press lets the
pilot bind the value to the current flight mode, which calls the root
signalStabiFM/signalStabiFMQuery signals from
Spirit Controller (Stabi) internally. Unless you're
specifically building flight-mode-bindable settings, use the plain variants above.
Frame
import "qrc:/qml/Item/Frame" β plain panel backgrounds,
no interactivity. Both are pure display containers: no custom properties beyond layout, and no
signals at all.
GroupFrame
A rounded black panel at theme_frameopacity, sized via
Layout.preferredWidth/Layout.preferredHeight (defaults 800Γ400) or
stretched with Layout.fillWidth/Layout.fillHeight β this is the
frame behind the project template's "Hello World!" label.
No custom properties or signals β position and size it like any
Item/layout child.
DlgFrame
A white, rounded 640Γ260 card that fades in to 95% opacity whenever it
becomes visible β the backing card behind DlgWindow and
OptWindow.
No custom properties or signals β the fade-in is automatic
(onVisibleChanged internally), triggered purely by toggling visible.
Window
import "qrc:/qml/Item/Window" β titled display panels
(TitleWindow, TelemWindow, ModelWindow) and modal
dialogs (DlgWindow, OptWindow) built on
DlgFrame.
TitleWindow
220Γ200 panel with a title bar (glowing centered label) over a content
area below it. Purely decorative β put your own children inside it.
Property
Type
Description
text
string
Title-bar caption.
No signals.
Start content at y: 50, and leave 15px at the bottom
The title bar itself is 48px tall β start placing your own children at
y: 50 (a couple pixels of clearance below it) rather than y: 0,
or they'll render underneath/behind the title label instead of in the content area below
it. Leave at least 15px of clearance from the panel's own bottom edge too -
don't let content run flush to it. The default height: 200 isn't fixed;
override it (e.g. TitleWindow { height: 280; ... }) so everything you place
actually fits between the title and that bottom margin rather than cramming it into the
default size or letting it overlap either edge.
TelemWindow
Same layout as TitleWindow (220Γ200, titled panel, same
y: 50 content offset and 15px bottom clearance), named for its use in telemetry
screens.
Property
Type
Description
telemVar
string
Title-bar caption (same role as TitleWindow.text, just named for its typical use).
No signals.
ModelWindowNote
490Γ235 titled panel pre-wired to the current model: title bar shows
mdl_cur_name, content area shows model_image_path (both root
properties, read automatically β you don't pass them in). This is exactly the component the
Home Screen template uses on its left side.
Tapping it navigates the shared stack directly, rather than emitting a
signal for you to handle: a single tap pushes /qml/Model/SelView.qml (the model
picker) and a press-and-hold pushes /qml/Model/ImageView.qml. There's nothing to
read out β if you need different behavior, build a custom window from
DlgFrame or TitleWindow instead.
No custom properties or signals exposed for you to hook into.
DlgWindow
A modal Yes/No confirmation dialog: dims the background, shows a
title and question, and closes itself (visible = false) after either answer.
Property
Type
Description
titleText
string
Dialog title.
questionText
string
Body text, word-wrapped.
answerYes / answerNo
string
Button labels β default to "Yes"/"No", translated.
Signal
Handled by
Reading the value
signal clickedYes()
You, in your own QML
No parameters β act directly (the dialog has already started closing itself).
signal clickedNo()
You, in your own QML
Same β commonly left empty.
Real usage, from the built-in language-change confirmation β note the
currentHomePath fallback pattern from Home
Screen apps:
DlgWindow {
id: sellangdlg
visible: false
titleText: qsTr("Select Language")
questionText: qsTr("Do you wish to change language to %1?").arg(name)
onClickedYes: {
signalLangSet(lang_index)
stack.push (currentHomePath || "/qml/Base/BaseView.qml")
}
onClickedNo: { }
}
OptWindow
A modal single-column list picker β dims the background and shows one
row per entry in list; tapping a row (or anywhere outside the dialog) closes it.
Property
Type
Description
list
var
A model (e.g. ListModel) whose entries each expose a name role, shown one per row.
Signal
Handled by
Reading the value
signal selectedId(int id)
You, in your own QML
Fires with the tapped row's index. Read it as the handler's implicit id parameter, or name it explicitly: onSelectedId: (id) => { ... }.
Real usage, from the battery-manager's "add battery" picker:
import "qrc:/qml/Item/Curve" β the channel-curve
preview used in the throttle/pitch model menus: a small canvas graph plus a moving cursor
showing where the current input sits on the curve.
CurveNote
Purely a display β it has no signals at all. Every property is
an input you drive from your own data; internally it just redraws (via one of eleven
CurveType-0.qmlβ¦CurveType-10.qml canvas renderers, picked by
type) whenever a bound value changes.
Property
Type
Description
type
int
Curve shape: 0 = linear (2-point), 1 = flat/expo, 2β9 = an N-point curve (type points), 10 = a further built-in shape. Matches the built-in curve_type channel setting.
val_0 β¦ val_8
double
Y-offset of each curve point (up to 9, depending on type) β bind these to your own per-point config values.
value
double
Current output value, shown as a percentage label below the graph.
input
double
Current input position, drawn as a moving vertical cursor line.
expo
double
Expo amount, for curve types that use it.
reverse
bool
Mirrors the curve horizontally.
point
int
Index of a point to highlight (e.g. the one currently being edited elsewhere on screen), or -1 for none.
Real usage, from the throttle curve view β note the values come from a
channel model, not from any signal on Curve itself:
To let the pilot actually edit a curve point's value, pair a
Curve with an AdjusterFloat bound to the
same val_N β the built-in menus set Curve.point to whichever index
the adjuster currently has focused, so the matching point highlights while it's being edited.
Label
import "qrc:/qml/Item/Label" β one component,
GlowingLabel. For plain text without a glow, just use Qt Quick Controls'
ordinary Label directly (as the project template does) β no import needed.
GlowingLabel
A Label with a soft colored glow behind the text, wrapped
in an Item that reports a precise bounding box (via TextMetrics) so
large font sizes still lay out correctly β this is what every titled
Window component and Icon caption uses for its
text.
Property
Type
Description
text
string
Alias straight through to the inner Label.text.
font
font
Alias to the inner Label.font β set font.pixelSize, etc. as usual.
color
color
Text color. Default white.
glowColor
color
Glow color. Defaults to the root colorGlow theme property.
spread
double
Glow spread/intensity. Default 0.1.
horizontalAlignment / verticalAlignment
enum
Aliased through to the inner Label.
label
Label
Alias to the inner Label itself, for properties not otherwise aliased β e.g. label.elide, label.width (used for ellipsizing long captions, as in MenuIcon).
No signals β it's styled text, not an interactive control.
TextField
No themed qrc:/qml/Item/ component for this one β use Qt
Quick Controls' TextField or TextInput directly, no import needed
beyond what the project template already has. The one thing that isn't automatic is the
on-screen keyboard, covered below.
No keyboard - use sticks or the touchscreenCaution
The radio has no keyboard, and nothing emulates one β there's no
arrow-key/WASD-style input at all, so don't build a control scheme around
Keys.onPressed or similar expecting it to work. For anything needing directional
or analog control (a game, a custom stick-driven widget), read input_ctrl
directly β Stick LV/Stick LH or Stick RV/
Stick RH are the natural choice, the same physical sticks the pilot already uses
to fly (see Reading control inputs for the full
index table). The touchscreen itself works normally for anything else β taps, drags,
MouseArea, all the usual QtQuick input handling.
Text entry β the virtual keyboard
For actual text β a name, a search box, anything typed β there's a
built-in on-screen keyboard and it works well. A plain TextField or
TextInput is all you need on your end; showing and hiding the keyboard panel
itself is manual, via two root functions, called directly with no prefix:
Function
What it does
keyboardShow()
Shows the on-screen keyboard panel.
keyboardHide()
Hides it.
It doesn't appear automatically just because a field gained focus β call
keyboardShow() yourself when your text-entry view appears, and
keyboardHide() on the field's onAccepted, on back-navigation, and in
Component.onDestruction, so it's never left showing over a screen that no longer
has anything to type into:
TextField {
id: nameField
y: 40// see the callout below
Component.onCompleted: keyboardShow ()
Component.onDestruction: keyboardHide ()
onAccepted: keyboardHide ()
}
The panel covers almost the whole screen
The keyboard panel is 440px tall out of the radio's 480px
screen height while shown β everything except the top status bar. Position your
TextField near the top of your view (roughly y: 20β80,
matching the built-in text-entry screens) so it's still visible above the keyboard once it
slides up, and don't expect anything lower on the screen to stay usable while it's open.