ActionResolver for DefaultActionResolver {
+ fn resolve(&mut self, ctx: ResolveContext
) -> P::Action {
ctx.action
}
}
+
diff --git a/src/orchestrator/core.rs b/src/orchestrator/core.rs
index 69db88f..735873c 100644
--- a/src/orchestrator/core.rs
+++ b/src/orchestrator/core.rs
@@ -1,65 +1,133 @@
-// path_from_the_root: src/orchestrator/core.rs
-
-extern crate alloc;
+// src/orchestrator/core.rs
+#[cfg(feature = "alloc")]
use alloc::boxed::Box;
-use alloc::string::String;
+#[cfg(feature = "alloc")]
use alloc::vec::Vec;
-use crate::component::{Component, ComponentError};
+use crate::component::ComponentError;
+use crate::focus::FocusManager;
use crate::input::{Bindings, Key};
use crate::orchestrator::{
ActionResolver, CommandHandler, CommandResult, DefaultActionResolver, DefaultCommandHandler,
DefaultStateCoordinator, EventBus, ModeName, ModeStack, ResolveContext, Router, RouterEvent,
StateCoordinator,
};
+use crate::page::Page;
-pub struct Orchestrator {
- router: Router,
- bindings: Bindings,
- action_resolver: Box>,
- command_handler: Box>,
- state_coordinator: Box>,
- modes: ModeStack,
- event_bus: EventBus,
+/// Main orchestrator with configurable capacity.
+///
+/// # Const Generics (for `no_std` without `alloc`)
+/// - `PAGES`: Maximum registered pages (default: 8)
+/// - `HISTORY`: Maximum navigation history (default: 16)
+/// - `FOCUS`: Maximum focus targets per page (default: 16)
+/// - `BINDINGS`: Maximum key bindings (default: 32)
+/// - `MODES`: Maximum mode stack depth (default: 8)
+/// - `EVENTS`: Maximum pending events (default: 8)
+///
+/// With `alloc` feature, these limits don't apply and trait objects are used
+/// for extensibility (ActionResolver, CommandHandler, StateCoordinator).
+///
+/// Without `alloc`, concrete default implementations are used.
+pub struct Orchestrator<
+ P: Page,
+ const PAGES: usize = 8,
+ const HISTORY: usize = 16,
+ const FOCUS: usize = 16,
+ const BINDINGS: usize = 32,
+ const MODES: usize = 8,
+ const EVENTS: usize = 8,
+> {
+ router: Router,
+ bindings: Bindings,
+
+ #[cfg(feature = "alloc")]
+ action_resolver: Box>,
+ #[cfg(not(feature = "alloc"))]
+ action_resolver: DefaultActionResolver,
+
+ #[cfg(feature = "alloc")]
+ command_handler: Box>,
+ #[cfg(not(feature = "alloc"))]
+ command_handler: DefaultCommandHandler,
+
+ #[cfg(feature = "alloc")]
+ state_coordinator: Box>,
+ #[cfg(not(feature = "alloc"))]
+ state_coordinator: DefaultStateCoordinator,
+
+ modes: ModeStack,
+
+ #[cfg(feature = "alloc")]
+ event_bus: EventBus,
+ #[cfg(not(feature = "alloc"))]
+ event_bus: EventBus,
+
running: bool,
}
-impl Default for Orchestrator
+impl<
+ P: Page + 'static,
+ const PAGES: usize,
+ const HISTORY: usize,
+ const FOCUS: usize,
+ const BINDINGS: usize,
+ const MODES: usize,
+ const EVENTS: usize,
+> Default for Orchestrator
where
- C::Action: Clone + 'static,
+ P::Action: Clone + 'static,
{
fn default() -> Self {
Self::new()
}
}
-impl Orchestrator
+impl<
+ P: Page + 'static,
+ const PAGES: usize,
+ const HISTORY: usize,
+ const FOCUS: usize,
+ const BINDINGS: usize,
+ const MODES: usize,
+ const EVENTS: usize,
+> Orchestrator
where
- C::Action: Clone + 'static,
- C::Event: Clone,
+ P::Action: Clone + 'static,
+ P::Event: Clone,
{
pub fn new() -> Self {
Self {
router: Router::new(),
bindings: Bindings::new(),
+ #[cfg(feature = "alloc")]
action_resolver: Box::new(DefaultActionResolver),
+ #[cfg(not(feature = "alloc"))]
+ action_resolver: DefaultActionResolver,
+ #[cfg(feature = "alloc")]
command_handler: Box::new(DefaultCommandHandler::default()),
+ #[cfg(not(feature = "alloc"))]
+ command_handler: DefaultCommandHandler::default(),
+ #[cfg(feature = "alloc")]
state_coordinator: Box::new(DefaultStateCoordinator),
+ #[cfg(not(feature = "alloc"))]
+ state_coordinator: DefaultStateCoordinator::default(),
modes: ModeStack::new(),
event_bus: EventBus::new(),
running: true,
}
}
- pub fn register_page(&mut self, id: String, page: C) {
- self.router.register(id, page);
+ /// Register a page. Returns false if capacity exceeded (no_std only).
+ pub fn register(&mut self, page: P) -> bool {
+ self.router.register(page)
}
- pub fn navigate_to(&mut self, id: String) -> Result<(), ComponentError> {
+ /// Navigate to a page variant. The associated data is ignored for lookup.
+ pub fn navigate_to(&mut self, target: P) -> Result<(), ComponentError> {
if let Some(RouterEvent::Navigated { from, to }) = self
.router
- .navigate(id.clone())
+ .navigate(target)
.map_err(|_| ComponentError::InvalidFocus)?
{
let _ = self.state_coordinator.on_navigate(from, to);
@@ -67,6 +135,19 @@ where
Ok(())
}
+ /// Navigate to a page, registering it first if not already registered.
+ pub fn navigate_or_register(&mut self, page: P) -> Result<(), ComponentError> {
+ if let Some(RouterEvent::Navigated { from, to }) = self
+ .router
+ .navigate_or_register(page)
+ .map_err(|_| ComponentError::InvalidFocus)?
+ {
+ let _ = self.state_coordinator.on_navigate(from, to);
+ }
+ Ok(())
+ }
+
+ /// Go back in navigation history.
pub fn back(&mut self) -> Result<(), ComponentError> {
if let Some(RouterEvent::Back { to }) = self
.router
@@ -78,6 +159,7 @@ where
Ok(())
}
+ /// Go forward in navigation history.
pub fn forward(&mut self) -> Result<(), ComponentError> {
if let Some(RouterEvent::Forward { to }) = self
.router
@@ -89,11 +171,14 @@ where
Ok(())
}
- pub fn bind(&mut self, key: Key, action: C::Action) {
+ /// Bind a key to an action.
+ pub fn bind(&mut self, key: Key, action: P::Action) {
self.bindings.bind(key, action);
}
- pub fn process_frame(&mut self, key: Key) -> Result, ComponentError> {
+ /// Process a frame with a key input.
+ #[cfg(feature = "alloc")]
+ pub fn process_frame(&mut self, key: Key) -> Result, ComponentError> {
if !self.running {
return Ok(Vec::new());
}
@@ -115,12 +200,12 @@ where
} else if let Some(action) = self.bindings.get(&key) {
let action = action.clone();
- if let Some(_) = self.router.current_id() {
- let component = self.router.current().ok_or(ComponentError::NoComponent)?;
+ if self.router.current().is_some() {
+ let page = self.router.current().ok_or(ComponentError::NoComponent)?;
let focus = self.router.focus_manager().current().ok_or(ComponentError::NoComponent)?;
let ctx = ResolveContext {
- component,
+ page,
focus,
action,
};
@@ -139,15 +224,61 @@ where
Ok(events)
}
- fn handle_action(&mut self, action: C::Action) -> Result