Focus Management
Hosanna UI has one active focus owner at a time. Platform input adapters normalize remote, keyboard, debug, mouse, and touch input into framework input events, then FocusManager routes those events through the focused view and its parent chain.
Use declarative focus hints first. Override focus hooks only when a layout needs rules that cannot be expressed with isInitialFocus, nextFocusMap, or focused-child restoration.
Focus Resolution Flow
Use isInitialFocus and nextFocusMap for most layouts. Use onInputEvent or onFindNextFocusable only when the view owns custom input or navigation behavior.
Core Concepts
FocusManager.setFocus(view)moves framework focus to a view.FocusManager.handleInputEvent(event)handles normalizedHsInputEventobjects from input adapters.BaseView.onInputEvent(event)receives non-direction keys and direction keys before focus resolution.BaseView.findNextFocusable(event)resolves a directional move.nextFocusMapmaps directions such asleft,right,up,down, anddefaultto a target id orNextViewFocustoken.requestFocusedChild(target, childId?)stores or applies a preferred child for composite views.
Input Events
View handlers receive HsInputEvent, which includes the normalized key, key state, direction, source adapter, long-press metadata, and consumption flags.
import { Key } from 'hosanna-ui/hosanna-bridge-lib/api';
import { KeyState, type HsInputEvent } from 'hosanna-ui/hosanna-bridge-lib/IFocusManager';
Button({ id: 'play', text: 'Play' })
.onInputEvent((event: HsInputEvent) => {
if (event.key === Key.Ok && event.keyState === KeyState.Press) {
this.play();
event.preventDefault = true;
event.consumed = true;
}
});
Set event.preventDefault = true when the view handled the event and the default focus behavior should stop.
Declarative Focus Maps
Use nextFocusMap to steer directional navigation. Map a direction to a sibling view id, or use a NextViewFocus token.
VGroup([
Button({ id: 'ok', text: 'OK' }).isInitialFocus(true),
Button({ id: 'cancel', text: 'Cancel' }),
])
.onFocus(event => {
event.nextFocusId = 'ok';
})
.nextFocusMap({
left: 'cancel',
right: 'ok',
default: NextViewFocus.Maintain,
});
NextViewFocus.Maintain keeps focus inside the current view. NextViewFocus.Exit jumps out of a view-owner boundary. NextViewFocus.None continues bubbling focus resolution to the parent.
Focused Children
Composite views can remember or request which direct child should receive focus.
import { ChildFocusTarget } from 'hosanna-ui/views/lib/view-api';
this.requestFocusedChild(ChildFocusTarget.First);
this.requestFocusedChild(ChildFocusTarget.Last);
this.requestFocusedChild(ChildFocusTarget.Default);
this.requestFocusedChild(ChildFocusTarget.Specific, 'detailsButton');
If the composite is already in the focus chain, Hosanna moves focus immediately. Otherwise it stores focusedChildId and uses it the next time focus cascades into the view.
setFocusedSubview(id) is still available inside BaseView subclasses as a concise helper for ChildFocusTarget.Specific.
Custom Resolution
Use onFindNextFocusable for layouts that need runtime decisions.
GridGroup({ id: 'grid', numCols: 3 }, cells)
.onFindNextFocusable(event => {
if (event.direction === Direction.Up && this.isOnFirstRow()) {
return this.getSubView('toolbar');
}
return undefined;
})
.nextFocusMap({
default: NextViewFocus.Maintain,
});
Return a focusable view, a NextViewFocus token, or undefined to let parent views continue resolving.
View Hooks
onFocus(event): called when a view enters focus; setevent.nextFocusIdto guide child focus.onBlur(): called when a view leaves focus.onInputEvent(event): handles normalized input events.onFocusedChildChange(event): observes focus movement inside a composite view.onFindNextFocusable(event): chooses the next focus target for directional movement.onRestoreFocus(event): customizes restoration when a composite receives focus again.
Ui And Interaction Focus Management
@view('UiAndInteractionFocusManagement')
export class UiAndInteractionFocusManagementView extends BaseExampleScreenView<UiAndInteractionFocusManagementState> {
protected getViews(): ViewStruct<ViewState>[] {
// --- edit below ---
return [
VGroup([
Label({ text: 'Focus Manager - onFocus/onBlur + nextFocusMap' }),
HGroup([
Button({ id: 'ok', text: 'OK', width: 220, height: 60 })
.isInitialFocus(true)
.onFocus((e: FocusEvent) => { e.nextFocusId = 'ok'; }),
Button({ id: 'cancel', text: 'Cancel', width: 220, height: 60 })
.onBlur(() => { /* blur side-effects go here */ }),
])
.itemSpacing(16)
.nextFocusMap({ left: 'cancel', right: 'ok', default: NextViewFocus.Maintain }),
])
.itemSpacing(12)
.translation([50, 50])
];
// --- edit above ---
}
}Focus Classes
Use the API reference for exact type signatures: FocusManager, IFocusManager, HsInputEvent, ViewFocusMap, NextViewFocus, and ChildFocusTarget.
CollectionView Focus
CollectionView owns row and item focus internally. Application code usually listens to row/item events or updates the data source instead of directly moving cell focus.
Use CollectionView APIs such as jumpToRow, scrollToRow, getFocusedDataSourceRow, and getFocusedDataSourceItem when restoring position, opening deep links, or responding to selection.