Focus Management
Introduction
Focus management is a critical aspect of the Maestro Web SDK, especially for BBD (Browser Based Devices) applications that need to support keyboard and remote navigation. The SDK provides mechanisms for managing focus between your application and the SDK's UI components, ensuring a seamless navigation experience.
SDK Architecture
The MaestroKit Web SDK uses the MVVM (Model-View-ViewModel) architecture pattern to separate business logic from UI rendering. View models manage state and provide methods for interacting with the SDK, while views handle the UI rendering and user interaction.
┌─────────────────────────────────┐
│ Parent App │
└─────────────────────────────────┘
▲ ▼
┌─────────────────────────────────┐
│ Maestro SDK │
│ │
│ ┌─────────┐ ┌────────────────┐ │
│ │ Models │ │ View Models │ │
│ └─────────┘ └────────────────┘ │
│ ▲ ▼ │
│ ┌──────────────────────────┐ │
│ │ UI Components │ │
│ └──────────────────────────┘ │
└─────────────────────────────────┘
Focus Architecture
The Maestro Web SDK uses the Norigin Spatial Navigation library for managing focus within its UI components. This library provides a grid-based navigation system that works well for TV-like interfaces where users navigate with directional (up/down/left/right) controls.
┌─────────────────────┐ ┌─────────────────────┐
│ │ │ │
│ Parent App UI │◄──Focus Transfer─► Maestro SDK UI │
│ │ │ │
└─────────────────────┘ └─────────────────────┘
Your Controls Panel Controls
Key Concepts
Focus Transfer:
Focus transfer occurs when navigation moves from your application's UI to the SDK's UI (and vice versa). This transfer needs to be handled explicitly to ensure a smooth user experience.Focus Entry Points:
When focus enters the SDK UI, it typically starts at the first focusable element in the panel. The SDK uses predefined focus IDs for key elements to ensure consistent focus behavior.Directional Navigation:
The SDK uses directional navigation (up/down/left/right) rather than tab-based navigation, which is more appropriate for TV-like interfaces controlled by remotes or game controllers.Autonomous Focus Regions:
The SDK UI operates as an autonomous focus region, handling internal focus navigation independently from your application. When focus leaves this region, control is handed back to your application.
Focus Navigation Patterns
Transferring Focus to the SDK
When the user navigates to the panel, transfer focus to the SDK:
// Example of transferring focus to the SDK
<button
className="button"
id="toggle-tools"
onKeyDown={eventHandler({
onRight: (event) => {
if (isShowingPanel) {
// Blur the current element
(event.target as HTMLElement).blur();
// Transfer focus to the SDK
vm.eventDriver.startFocusManagement();
}
},
onBottom: focusLiveButton,
onLeft: focusPanel,
})}
>
Tools
</button>
Receiving Focus from the SDK
Implement the startFocusManagement method in your event delegate:
class MyEventDelegate implements IMaestroEventDelegate {
// Other methods...
startFocusManagement(): Promise<void> {
// Focus an appropriate element in your app
const toggleEvent = document.getElementById("toggle-event");
if (toggleEvent) {
(toggleEvent as HTMLElement).focus();
}
return Promise.resolve();
}
}
Component Integration
In a component-based architecture (React/Preact), bind the focus management to your component lifecycle:
// Using the event handler in component
<button
id="live-button"
disabled={vm.maestroEventDelegate.live.value}
onKeyDown={eventHandler({
onEnter: goLive,
onTop: focusEvent,
onRight: (event) => {
if (isShowingPanel) {
(event.target as HTMLElement).blur();
vm.eventDriver.startFocusManagement();
}
},
})}
>
{live ? "You're Live " : "Go Live"}
</button>
Implementation Example
Here's a simplified example of how focus transfer is typically implemented:
// In your parent application
class MyEventDelegate implements IMaestroEventDelegate {
startFocusManagement(): Promise<void> {
// When focus returns from SDK to your app,
// focus an appropriate element
const appControl = document.getElementById('app-control');
if (appControl) {
appControl.focus();
}
return Promise.resolve();
}
// ... other methods ...
}
// When navigating from your app to the SDK
function navigateToPanel() {
// Get the event view model
const eventViewModel = SDK.getMaestroEventViewModel();
// Blur the currently focused element in your app
(document.activeElement as HTMLElement)?.blur();
// Start focus management in the SDK
eventViewModel.startFocusManagement();
}