Controls
Hosanna UI controls are regular Hosanna views with a helpful opinionated API. They inherit the same lifecycle, state model, layout, and styling mechanisms as other views, then add control-specific behavior (events, keyboard handling, composition).
How Controls Work
- They extend views: Controls subclass the same
BaseView
as primitives and groups. You use them declaratively ingetViews()
. - Stateful styling: Controls read styles via
styleKey
, and apply the correct sub-style based onViewStatus
(e.g.,normal
,focused
,selected
). - Focus-aware: Each view has
isFocused
; the focus system updates it automatically.ViewStatus
tracks high-level interaction state and drives styles. - Custom data: Use
customData(...)
to attach arbitrary data. Retrieve it strongly typed at runtime withview.getCustomData<T>()
.
Most controls accept text
, style keys, and event callbacks. Add features incrementally to keep performance predictable.
Styling Controls with Stateful Styles
Controls use the same theme style system as all views. A styleKey
points to a style entry in your style.config.json
. Control styles typically live under controls.<ControlType>.<subStyle>
and contain per-status dictionaries:
{
"controls": {
"Button": {
"default": {
"normal": { "color": "~theme.colors.white" },
"focused": { "color": "~theme.colors.red" },
"selected": { "opacity": 0.9 }
},
"primary": {
"normal": { "titleFontKey": "~theme.fonts.text-bold-20" }
}
}
}
}
- styleKey: For example,
controls.Button.default
orcontrols.Button.primary
. - Stateful: Sub-keys such as
normal
,focused
,selected
,disabled
,error
,focusSelected
map toViewStatus
values. - Auto-application: When focus changes, the framework switches the active style sub-dictionary automatically.
Use controls.<ControlType>.<subStyle>
for control styles (e.g., controls.Button.default
). Keep tokens (~theme.colors.*
, ~theme.fonts.*
) for portability.
Focus, ViewStatus, and Styles
isFocused
is updated by the focus system. You do not set it manually.ViewStatus
can be controlled when needed (e.g., programmatically select a control), and the style engine uses it to pick the right sub-style.- Typical statuses:
normal
,focused
,selected
,disabled
,error
,focusSelected
.
Avoid changing view state inside animation ticks. Let the animator finish, then persist the final logical state.
Custom Data on Controls
Attach context to any view/control and retrieve it later without type assertions scattered across your code:
Button({ text: 'Details', styleKey: 'controls.Button.primary' })
.customData({ productId: item.id })
.onClick((event) => {
const data = event.view.getCustomData<{ productId: string }>();
this.openDetails(data.productId);
});
Example: Button + Stateful Styles
Button({
text: 'Click me',
styleKey: 'controls.Button.default',
width: 300,
height: 50,
})
.onClick(() => this.count++)
.customData({ kind: 'primary-action' });
- On focus,
ViewStatus
becomesfocused
, and the style engine applies thefocused
style automatically. - You can set
viewStatus(ViewStatus.Selected)
when you need a selected visual state.
Controls Index
Use the pages below for focused guides and live examples:
- Button
- CheckBox
- ComboBox
- ScrollView
- Popup
- MiniKeyboard