Wave Wave Developer Docs

Component Reference

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.

PropertyTypeDescription
checkedboolCurrent on/off state. Bindable both ways β€” set it to pre-check the box, read it to react to changes.
positionreal0.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.
pressedboolTrue while the thumb is being pressed.
enabledboolInherited from Item; dims and disables interaction when false.
SignalHandled byReading the value
onToggled()You, in your own QMLFires on a completed toggle. Read checked inside the handler.
onCheckedChanged()You, in your own QMLFires whenever checked changes, including programmatically β€” use this instead of onToggled if you also set checked from code.
onPositionChanged()You, in your own QMLThe 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:

CheckBox {
	enabled: chansel_index >= 0

	onPositionChanged: {
		if (position == 0.0 || position == 1.0)
			chanSet (chansel_index, "rev", parseInt (position))
	}
}

Icon

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.

PropertyTypeDescription
iconTextstringCaption shown under the icon, ellipsized if it overflows.
iconPathstringImage source (loaded asynchronously).
enabledboolInherited; dims the icon image to 50% opacity when false (it still catches taps unless you separately gate onClicked).
SignalHandled byReading the value
signal action()You, in your own QMLFires 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.

PropertyTypeDescription
iconTextstringDeclared for consistency with the other Icon components but not displayed by PanelIcon itself.
iconPathstringImage source (loaded asynchronously).
SignalHandled byReading the value
signal action()You, in your own QMLSame 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.

PropertyTypeDescription
iconTextstringCaption drawn centered over the image.
iconPathstringImage source.
SignalHandled byReading the value
signal action()You, in your own QMLSame 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.

PropertyTypeDescription
textstringLabel text, centered and word-wrapped.
pressedboolRead-only; true while held down (darkens the gradient).
enabledboolInherited; grays the label and stops it reacting to taps.
SignalHandled byReading the value
signal clicked()You, in your own QMLNo 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.

PropertyTypeDescription
textstringLabel text.
pressedboolRead-only press state.
SignalHandled byReading the value
signal clicked()You, in your own QMLNo parameters.
FeatureButton Note

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.

PropertyTypeDescription
checkable / checkedboolInherited 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).
textstringInherited label text.
SignalHandled byReading the value
onClicked() / onToggled()You, in your own QMLStandard 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.

PropertyTypeDescription
valueintThe current number. Set it in your onGetValue handler; read it in onSetValue.
range_min / range_maxintWheel bounds. Defaults 0–100.
stepintWheel increment. Default 1.
postfixstringUnit suffix appended after the number, e.g. "%" or "GMT".
sideintPassed straight through to the value-picker's own layout β€” leave at 0 unless you're matching a specific built-in picker's positioning.
liveboolWhen true, setValue() fires continuously while the wheel spins, not just on confirm β€” use for settings that should preview live (e.g. a volume level).
SignalHandled byReading the value
signal getValue()You, in your own QMLSet value from your own data here, right before the wheel opens.
signal setValue()You, in your own QMLRead value here (already updated) and persist it β€” for your own app's settings, typically via signalAppsCfgSet.

Real usage, from the built-in timezone setting:

Adjuster {
	range_min: -12
	range_max: 12
	postfix: "GMT"

	onGetValue: {
		value = cfg_val - 12
	}

	onSetValue: {
		cfgSet (3, value + 12)
	}
}
AdjusterFloat

Same interface as Adjuster, for fractional values.

PropertyTypeDescription
value / range_min / range_max / stepdoubleSame roles as Adjuster, as floating-point.
decimalsintDigits shown after the decimal point. Default 1.
postfix / side / liveβ€”Same as Adjuster.
SignalHandled byReading the value
signal getValue() / signal setValue()You, in your own QMLIdentical 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.

PropertyTypeDescription
textstringTitle-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.

PropertyTypeDescription
telemVarstringTitle-bar caption (same role as TitleWindow.text, just named for its typical use).

No signals.

ModelWindow Note

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.

PropertyTypeDescription
titleTextstringDialog title.
questionTextstringBody text, word-wrapped.
answerYes / answerNostringButton labels β€” default to "Yes"/"No", translated.
SignalHandled byReading the value
signal clickedYes()You, in your own QMLNo parameters β€” act directly (the dialog has already started closing itself).
signal clickedNo()You, in your own QMLSame β€” 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.

PropertyTypeDescription
listvarA model (e.g. ListModel) whose entries each expose a name role, shown one per row.
SignalHandled byReading the value
signal selectedId(int id)You, in your own QMLFires 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:

OptWindow {
	list: ListModel {
		ListElement { name: qsTr ("New Battery") }
		ListElement { name: qsTr ("Existing Battery") }
	}

	onSelectedId: {
		if (id == 0)
			stack.push ("/qml/Model/BatMan/BatAddView.qml")
		else
			stack.push ("/qml/Model/BatMan/BatExistView.qml")
	}
}

Curve

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.

Curve Note

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.

PropertyTypeDescription
typeintCurve 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_8doubleY-offset of each curve point (up to 9, depending on type) β€” bind these to your own per-point config values.
valuedoubleCurrent output value, shown as a percentage label below the graph.
inputdoubleCurrent input position, drawn as a moving vertical cursor line.
expodoubleExpo amount, for curve types that use it.
reverseboolMirrors the curve horizontally.
pointintIndex 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:

Curve {
	input: input_ctrl[chan_list.get(throttle_chan).input]
	value: thr_val
	type: curve_type

	val_0: chan_list.get(throttle_chan).curve_val_0
	val_1: chan_list.get(throttle_chan).curve_val_1
	// … val_2 … val_8

	reverse: chan_list.get(throttle_chan).rev
}

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.

PropertyTypeDescription
textstringAlias straight through to the inner Label.text.
fontfontAlias to the inner Label.font β€” set font.pixelSize, etc. as usual.
colorcolorText color. Default white.
glowColorcolorGlow color. Defaults to the root colorGlow theme property.
spreaddoubleGlow spread/intensity. Default 0.1.
horizontalAlignment / verticalAlignmentenumAliased through to the inner Label.
labelLabelAlias 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 touchscreen Caution

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:

FunctionWhat 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.