diff --git a/anathema-default-widgets/src/testing.rs b/anathema-default-widgets/src/testing.rs index 5a7e12b3..ba6c3032 100644 --- a/anathema-default-widgets/src/testing.rs +++ b/anathema-default-widgets/src/testing.rs @@ -3,7 +3,7 @@ use anathema_backend::Backend; use anathema_geometry::{Pos, Size}; use anathema_state::{State, StateId, States, Value}; use anathema_templates::blueprints::Blueprint; -use anathema_templates::{Document, Globals}; +use anathema_templates::{Document, Globals, ToSourceKind}; use anathema_widgets::components::ComponentRegistry; use anathema_widgets::layout::text::StringStorage; use anathema_widgets::layout::{layout_widget, position_widget, Constraints, LayoutCtx, LayoutFilter, Viewport}; @@ -44,7 +44,7 @@ impl TestRunner { @main "; let mut doc = Document::new(root); - let main = doc.add_component("main", src).unwrap(); + let main = doc.add_component("main", src.to_template()).unwrap(); components.add_component(main.into(), (), ()); let (blueprint, globals) = doc.compile().unwrap(); diff --git a/anathema-runtime/src/events.rs b/anathema-runtime/src/events.rs index 7889d28d..03972ac4 100644 --- a/anathema-runtime/src/events.rs +++ b/anathema-runtime/src/events.rs @@ -5,7 +5,7 @@ use anathema_geometry::Size; use anathema_state::{AnyState, States}; use anathema_store::storage::strings::Strings; use anathema_widgets::components::events::{Event, KeyCode, KeyEvent, KeyState}; -use anathema_widgets::components::{AssociatedEvents, Context, Emitter}; +use anathema_widgets::components::{AssociatedEvents, Emitter, UntypedContext}; use anathema_widgets::layout::{Constraints, Viewport}; use anathema_widgets::{AttributeStorage, Components, Elements, WidgetKind, WidgetTree}; @@ -60,7 +60,7 @@ impl EventHandler { let Some((node, values)) = tree.get_node_by_path(path) else { return }; let elements = Elements::new(node.children(), values, attribute_storage); - let context = Context { + let context = UntypedContext { emitter, viewport: *viewport, assoc_events, @@ -99,7 +99,7 @@ impl EventHandler { let Some((node, values)) = tree.get_node_by_path(path) else { return }; let elements = Elements::new(node.children(), values, attribute_storage); - let context = Context { + let context = UntypedContext { emitter, viewport: *viewport, assoc_events, @@ -149,7 +149,7 @@ impl EventHandler { let Some((node, values)) = tree.get_node_by_path(path) else { return }; let elements = Elements::new(node.children(), values, attribute_storage); - let context = Context { + let context = UntypedContext { emitter, viewport: *viewport, assoc_events, @@ -161,7 +161,7 @@ impl EventHandler { component .dyn_component - .any_callback(state, event_ident, common_val, elements, context); + .any_receive(state, event_ident, common_val, elements, context); }); }) } @@ -219,7 +219,7 @@ pub fn global_event<'bp, T: Backend>( let Some((node, values)) = tree.get_node_by_path(path) else { return }; let elements = Elements::new(node.children(), values, attribute_storage); let state = states.get_mut(state_id); - let context = Context { + let context = UntypedContext { emitter, viewport, assoc_events, @@ -244,7 +244,7 @@ pub fn global_event<'bp, T: Backend>( let Some((node, values)) = tree.get_node_by_path(path) else { return }; let elements = Elements::new(node.children(), values, attribute_storage); let state = states.get_mut(state_id); - let context = Context { + let context = UntypedContext { emitter, viewport, assoc_events, @@ -275,7 +275,7 @@ pub fn global_event<'bp, T: Backend>( let Some((node, values)) = tree.get_node_by_path(path) else { return }; let elements = Elements::new(node.children(), values, attribute_storage); let state = states.get_mut(state_id); - let context = Context { + let context = UntypedContext { emitter, viewport, assoc_events, diff --git a/anathema-runtime/src/lib.rs b/anathema-runtime/src/lib.rs index b50aa6a8..7fe0e191 100644 --- a/anathema-runtime/src/lib.rs +++ b/anathema-runtime/src/lib.rs @@ -18,7 +18,6 @@ // ----------------------------------------------------------------------------- use std::fmt::Write; -use std::path::PathBuf; use std::sync::atomic::{AtomicBool, Ordering}; use std::time::{Duration, Instant}; @@ -30,15 +29,15 @@ use anathema_state::{ }; use anathema_store::tree::{root_node, AsNodePath}; use anathema_templates::blueprints::Blueprint; -use anathema_templates::{Document, Globals}; +use anathema_templates::{Document, Globals, ToSourceKind}; use anathema_widgets::components::{ - AssociatedEvents, Component, ComponentId, ComponentKind, ComponentRegistry, Context, Emitter, ViewMessage, + AssociatedEvents, Component, ComponentId, ComponentKind, ComponentRegistry, Emitter, UntypedContext, ViewMessage, }; use anathema_widgets::layout::text::StringStorage; use anathema_widgets::layout::{layout_widget, position_widget, Constraints, LayoutCtx, LayoutFilter, Viewport}; use anathema_widgets::{ - eval_blueprint, try_resolve_future_values, update_tree, AnyWidget, AttributeStorage, Attributes, Components, - Elements, EvalContext, Factory, FloatingWidgets, Scope, Widget, WidgetKind, WidgetTree, + eval_blueprint, try_resolve_future_values, update_tree, AttributeStorage, Components, Elements, EvalContext, + Factory, FloatingWidgets, Scope, WidgetKind, WidgetTree, }; use events::EventHandler; use notify::{recommended_watcher, Event, RecommendedWatcher, RecursiveMode, Watcher}; @@ -63,36 +62,20 @@ impl RuntimeBuilder { pub fn register_component( &mut self, ident: impl Into, - template_path: impl Into, + template: impl ToSourceKind, component: C, state: C::State, ) -> Result> { let ident = ident.into(); - let id = self.document.add_component(ident, template_path.into())?.into(); + let id = self.document.add_component(ident, template.to_source_kind())?.into(); self.component_registry.add_component(id, component, state); Ok(id.into()) } - pub fn register_default( - &mut self, - ident: impl Into, - template_path: impl Into, - ) -> Result> - where - C: Component + Default + 'static, - C::State: Default, - { - let ident = ident.into(); - let id = self.document.add_component(ident, template_path.into())?.into(); - self.component_registry - .add_component(id, C::default(), C::State::default()); - Ok(id.into()) - } - pub fn register_prototype( &mut self, ident: impl Into, - template_path: impl Into, + template: impl ToSourceKind, proto: FC, state: FS, ) -> Result<()> @@ -102,17 +85,25 @@ impl RuntimeBuilder { C: Component + 'static, { let ident = ident.into(); - let id = self.document.add_component(ident, template_path.into())?.into(); + let id = self.document.add_component(ident, template.to_source_kind())?.into(); self.component_registry.add_prototype(id, proto, state); Ok(()) } - pub fn register_default_widget(&mut self, ident: &str) { - self.factory.register_default::(ident); - } - - pub fn register_widget(&mut self, ident: &str, factory: impl Fn(&Attributes<'_>) -> Box + 'static) { - self.factory.register_widget(ident, factory); + pub fn register_default( + &mut self, + ident: impl Into, + template: impl ToSourceKind, + ) -> Result> + where + C: Component + Default + 'static, + C::State: Default, + { + let ident = ident.into(); + let id = self.document.add_component(ident, template.to_source_kind())?.into(); + self.component_registry + .add_component(id, C::default(), C::State::default()); + Ok(id.into()) } pub fn emitter(&self) -> Emitter { @@ -316,6 +307,13 @@ where return; } + // use std::io::Write; + // let mut file = std::fs::OpenOptions::new() + // .append(true) + // .write(true) + // .open("/tmp/log.lol").unwrap(); + // file.write(format!("{}\n", self.changes.len()).as_bytes()).unwrap(); + let mut scope = Scope::new(); self.changes.drain().rev().for_each(|(sub, change)| { sub.iter().for_each(|sub| { @@ -367,7 +365,7 @@ where let Some((node, values)) = tree.get_node_by_path(path) else { return }; let elements = Elements::new(node.children(), values, attribute_storage); - let context = Context { + let context = UntypedContext { emitter: &self.emitter, viewport: self.viewport, assoc_events, @@ -453,7 +451,7 @@ where let Some((node, values)) = tree.get_node_by_path(path) else { return }; let elements = Elements::new(node.children(), values, &mut attribute_storage); - let context = Context { + let context = UntypedContext { emitter: &self.emitter, viewport: self.viewport, assoc_events: &mut assoc_events, @@ -505,7 +503,7 @@ where }); let mut document = Document::new(tpl); - let _component_id = document.add_component("errors", errors); + let _component_id = document.add_component("errors", errors.to_template()); let (blueprint, globals) = document.compile().expect("the error template can't fail"); self.blueprint = blueprint; self.globals = globals; @@ -697,7 +695,7 @@ where let Some((node, values)) = tree.get_node_by_path(path) else { return }; let elements = Elements::new(node.children(), values, attribute_storage); - let context = Context { + let context = UntypedContext { emitter: &self.emitter, viewport: self.viewport, assoc_events, diff --git a/anathema-state/src/common.rs b/anathema-state/src/common.rs index 530ac60b..c9b5a0dc 100644 --- a/anathema-state/src/common.rs +++ b/anathema-state/src/common.rs @@ -1,4 +1,5 @@ use std::fmt::{self, Display}; +use std::ops::Deref; use crate::{Hex, Number}; @@ -20,6 +21,14 @@ impl<'bp> CommonString<'bp> { } } +impl Deref for CommonString<'_> { + type Target = str; + + fn deref(&self) -> &str { + self.to_str() + } +} + impl AsRef for CommonString<'_> { fn as_ref(&self) -> &str { self.to_str() @@ -203,6 +212,21 @@ macro_rules! impl_try_from_int { }; } +macro_rules! impl_try_from_float { + ($t:ty) => { + impl TryFrom> for $t { + type Error = (); + + fn try_from(value: CommonVal<'_>) -> Result { + match value { + CommonVal::Float(n) => Ok(n as $t), + _ => Err(()), + } + } + } + }; +} + impl_try_from_int!(usize); impl_try_from_int!(isize); impl_try_from_int!(u64); @@ -214,6 +238,9 @@ impl_try_from_int!(i16); impl_try_from_int!(u8); impl_try_from_int!(i8); +impl_try_from_float!(f64); +impl_try_from_float!(f32); + #[cfg(test)] mod test { use std::rc::Rc; diff --git a/anathema-state/src/value/mod.rs b/anathema-state/src/value/mod.rs index d568ee9d..3e8aed53 100644 --- a/anathema-state/src/value/mod.rs +++ b/anathema-state/src/value/mod.rs @@ -204,6 +204,13 @@ impl ElementState { } } + fn try_as_ref(&self) -> Option<&T> { + match self { + Self::Dropped => unreachable!(), + Self::Alive(ref value) => value.to_any_ref().downcast_ref(), + } + } + fn drop_value(&mut self) { let _ = std::mem::take(self); } @@ -221,6 +228,10 @@ impl<'a, T> Shared<'a, T> { _p: PhantomData, } } + + pub fn try_as_ref(&self) -> Option<&T> { + self.state.inner.try_as_ref() + } } impl<'a, T> Deref for Shared<'a, T> { diff --git a/anathema-templates/src/components.rs b/anathema-templates/src/components.rs index e1b35f4e..9591ae67 100644 --- a/anathema-templates/src/components.rs +++ b/anathema-templates/src/components.rs @@ -16,11 +16,44 @@ use crate::token::Tokens; use crate::variables::Variables; use crate::Lexer; -pub(crate) enum SourceKind { +pub trait ToSourceKind { + fn to_path(self) -> SourceKind; + + fn to_template(self) -> SourceKind; + + fn to_source_kind(self) -> SourceKind + where + Self: Sized, + { + self.to_path() + } +} + +impl> ToSourceKind for T { + fn to_path(self) -> SourceKind { + SourceKind::Path(self.as_ref().into()) + } + + fn to_template(self) -> SourceKind { + SourceKind::Str(self.as_ref().into()) + } +} + +pub enum SourceKind { Path(PathBuf), Str(String), } +impl ToSourceKind for SourceKind { + fn to_path(self) -> SourceKind { + self + } + + fn to_template(self) -> SourceKind { + self + } +} + impl From for SourceKind { fn from(value: PathBuf) -> Self { Self::Path(value) diff --git a/anathema-templates/src/document.rs b/anathema-templates/src/document.rs index 282393ec..ae8c70e2 100644 --- a/anathema-templates/src/document.rs +++ b/anathema-templates/src/document.rs @@ -40,9 +40,8 @@ impl Document { } #[allow(private_bounds)] - pub fn add_component(&mut self, name: impl Into, src: impl Into) -> Result { + pub fn add_component(&mut self, name: impl Into, src: SourceKind) -> Result { let name = name.into(); - let src = src.into(); let component_src = match src { SourceKind::Str(s) => ComponentSource::InMemory(s), diff --git a/anathema-templates/src/lib.rs b/anathema-templates/src/lib.rs index f161055e..80e009a0 100644 --- a/anathema-templates/src/lib.rs +++ b/anathema-templates/src/lib.rs @@ -1,4 +1,4 @@ -pub use crate::components::WidgetComponentId; +pub use crate::components::{SourceKind, ToSourceKind, WidgetComponentId}; pub use crate::document::Document; pub use crate::expressions::Expression; pub use crate::lexer::Lexer; diff --git a/anathema-templates/src/statements/eval.rs b/anathema-templates/src/statements/eval.rs index de59c9ec..362d7af7 100644 --- a/anathema-templates/src/statements/eval.rs +++ b/anathema-templates/src/statements/eval.rs @@ -168,7 +168,7 @@ mod test { use super::*; use crate::document::Document; - use crate::single; + use crate::{single, ToSourceKind}; #[test] fn eval_node() { @@ -217,7 +217,7 @@ mod test { let comp_src = "node a + 2"; let mut doc = Document::new(src); - doc.add_component("comp", comp_src).unwrap(); + doc.add_component("comp", comp_src.to_template()).unwrap(); let (blueprint, _) = doc.compile().unwrap(); assert!(matches!(blueprint, Blueprint::Component(Component { .. }))); } @@ -239,7 +239,7 @@ mod test { "; let mut doc = Document::new(src); - doc.add_component("comp", comp_src).unwrap(); + doc.add_component("comp", comp_src.to_template()).unwrap(); let (blueprint, _) = doc.compile().unwrap(); assert!(matches!(blueprint, Blueprint::Component(Component { .. }))); } @@ -253,7 +253,7 @@ mod test { "; let mut doc = Document::new(src); - doc.add_component("comp", "node a").unwrap(); + doc.add_component("comp", "node a".to_template()).unwrap(); let _ = doc.compile().unwrap(); } } diff --git a/anathema-widgets/src/components/mod.rs b/anathema-widgets/src/components/mod.rs index 55dc9277..ab69d87a 100644 --- a/anathema-widgets/src/components/mod.rs +++ b/anathema-widgets/src/components/mod.rs @@ -1,5 +1,6 @@ use std::any::Any; use std::marker::PhantomData; +use std::ops::{Deref, DerefMut}; use std::time::Duration; use anathema_state::{AnyState, CommonVal, SharedState, State, StateId, Value}; @@ -163,38 +164,34 @@ impl Emitter { } } -pub struct Context<'rt> { - pub emitter: &'rt Emitter, - pub viewport: Viewport, - pub assoc_events: &'rt mut AssociatedEvents, - pub state_id: StateId, - pub parent: Option, - pub strings: &'rt Strings, - pub assoc_functions: &'rt [(StringId, StringId)], -} +pub struct Context<'rt, T>(UntypedContext<'rt>, PhantomData); + +impl<'rt, T: 'static> Context<'rt, T> { + fn new(context: UntypedContext<'rt>) -> Self { + Self(context, PhantomData) + } -impl<'rt> Context<'rt> { /// Publish event /// /// # Panics /// /// This will panic if the shared value is exclusively borrowed /// at the time of the invocation. - pub fn publish(&mut self, ident: &str, mut f: F) + pub fn publish(&mut self, ident: &str, mut f: F) where F: FnMut(&T) -> &Value + 'static, V: AnyState, { - let Some(internal) = self.strings.lookup(ident) else { return }; + let Some(internal) = self.0.strings.lookup(ident) else { return }; - let ids = self.assoc_functions.iter().find(|(i, _)| *i == internal); + let ids = self.0.assoc_functions.iter().find(|(i, _)| *i == internal); // If there is no parent there is no one to emit the event to. - let Some(parent) = self.parent else { return }; + let Some(parent) = self.0.parent else { return }; let Some((_, external)) = ids else { return }; - self.assoc_events.push( - self.state_id, + self.0.assoc_events.push( + self.0.state_id, parent, *external, Box::new(move |state: &dyn AnyState| -> SharedState<'_> { @@ -212,6 +209,36 @@ impl<'rt> Context<'rt> { }), ); } + + pub fn emit(&self, recipient: ComponentId, value: M) { + self.emitter + .emit(recipient, value) + .expect("this will not fail unless the runtime is droped") + } +} + +impl<'rt, T> Deref for Context<'rt, T> { + type Target = UntypedContext<'rt>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'rt, T> DerefMut for Context<'rt, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +pub struct UntypedContext<'rt> { + pub emitter: &'rt Emitter, + pub viewport: Viewport, + pub assoc_events: &'rt mut AssociatedEvents, + pub state_id: StateId, + pub parent: Option, + pub strings: &'rt Strings, + pub assoc_functions: &'rt [(StringId, StringId)], } pub struct AssociatedEvent { @@ -258,13 +285,31 @@ pub trait Component { type Message; #[allow(unused_variables, unused_mut)] - fn on_blur(&mut self, state: &mut Self::State, mut elements: Elements<'_, '_>, context: Context<'_>) {} + fn on_blur( + &mut self, + state: &mut Self::State, + mut elements: Elements<'_, '_>, + mut context: Context<'_, Self::State>, + ) { + } #[allow(unused_variables, unused_mut)] - fn on_focus(&mut self, state: &mut Self::State, mut elements: Elements<'_, '_>, context: Context<'_>) {} + fn on_focus( + &mut self, + state: &mut Self::State, + mut elements: Elements<'_, '_>, + mut context: Context<'_, Self::State>, + ) { + } #[allow(unused_variables, unused_mut)] - fn on_key(&mut self, key: KeyEvent, state: &mut Self::State, mut elements: Elements<'_, '_>, context: Context<'_>) { + fn on_key( + &mut self, + key: KeyEvent, + state: &mut Self::State, + mut elements: Elements<'_, '_>, + mut context: Context<'_, Self::State>, + ) { } #[allow(unused_variables, unused_mut)] @@ -273,12 +318,19 @@ pub trait Component { mouse: MouseEvent, state: &mut Self::State, mut elements: Elements<'_, '_>, - context: Context<'_>, + mut context: Context<'_, Self::State>, ) { } #[allow(unused_variables, unused_mut)] - fn tick(&mut self, state: &mut Self::State, mut elements: Elements<'_, '_>, context: Context<'_>, dt: Duration) {} + fn tick( + &mut self, + state: &mut Self::State, + mut elements: Elements<'_, '_>, + context: Context<'_, Self::State>, + dt: Duration, + ) { + } #[allow(unused_variables, unused_mut)] fn message( @@ -286,21 +338,27 @@ pub trait Component { message: Self::Message, state: &mut Self::State, mut elements: Elements<'_, '_>, - context: Context<'_>, + mut context: Context<'_, Self::State>, ) { } #[allow(unused_variables, unused_mut)] - fn resize(&mut self, state: &mut Self::State, mut elements: Elements<'_, '_>, context: Context<'_>) {} + fn resize( + &mut self, + state: &mut Self::State, + mut elements: Elements<'_, '_>, + mut context: Context<'_, Self::State>, + ) { + } #[allow(unused_variables, unused_mut)] - fn callback( + fn receive( &mut self, - state: &mut Self::State, - callback_ident: &str, + ident: &str, value: CommonVal<'_>, - elements: Elements<'_, '_>, - context: Context<'_>, + state: &mut Self::State, + mut elements: Elements<'_, '_>, + mut context: Context<'_, Self::State>, ) { } @@ -330,7 +388,7 @@ pub trait AnyComponent { ev: Event, state: Option<&mut dyn AnyState>, elements: Elements<'_, '_>, - context: Context<'_>, + context: UntypedContext<'_>, ) -> Event; fn any_message( @@ -338,30 +396,30 @@ pub trait AnyComponent { message: Box, state: Option<&mut dyn AnyState>, elements: Elements<'_, '_>, - context: Context<'_>, + context: UntypedContext<'_>, ); fn any_tick( &mut self, state: Option<&mut dyn AnyState>, elements: Elements<'_, '_>, - context: Context<'_>, + context: UntypedContext<'_>, dt: Duration, ); - fn any_focus(&mut self, state: Option<&mut dyn AnyState>, elements: Elements<'_, '_>, context: Context<'_>); + fn any_focus(&mut self, state: Option<&mut dyn AnyState>, elements: Elements<'_, '_>, context: UntypedContext<'_>); - fn any_blur(&mut self, state: Option<&mut dyn AnyState>, elements: Elements<'_, '_>, context: Context<'_>); + fn any_blur(&mut self, state: Option<&mut dyn AnyState>, elements: Elements<'_, '_>, context: UntypedContext<'_>); - fn any_resize(&mut self, state: Option<&mut dyn AnyState>, elements: Elements<'_, '_>, context: Context<'_>); + fn any_resize(&mut self, state: Option<&mut dyn AnyState>, elements: Elements<'_, '_>, context: UntypedContext<'_>); - fn any_callback( + fn any_receive( &mut self, state: Option<&mut dyn AnyState>, name: &str, value: CommonVal<'_>, elements: Elements<'_, '_>, - context: Context<'_>, + context: UntypedContext<'_>, ); fn accept_focus_any(&self) -> bool; @@ -377,11 +435,12 @@ where event: Event, state: Option<&mut dyn AnyState>, widgets: Elements<'_, '_>, - context: Context<'_>, + context: UntypedContext<'_>, ) -> Event { let state = state .and_then(|s| s.to_any_mut().downcast_mut::()) .expect("components always have a state"); + let context = Context::::new(context); match event { Event::Blur | Event::Focus => (), // Application focus, not component focus. @@ -402,26 +461,29 @@ where message: Box, state: Option<&mut dyn AnyState>, elements: Elements<'_, '_>, - context: Context<'_>, + context: UntypedContext<'_>, ) { let state = state .and_then(|s| s.to_any_mut().downcast_mut::()) .expect("components always have a state"); let Ok(message) = message.downcast::() else { return }; + let context = Context::::new(context); self.message(*message, state, elements, context); } - fn any_focus(&mut self, state: Option<&mut dyn AnyState>, elements: Elements<'_, '_>, context: Context<'_>) { + fn any_focus(&mut self, state: Option<&mut dyn AnyState>, elements: Elements<'_, '_>, context: UntypedContext<'_>) { let state = state .and_then(|s| s.to_any_mut().downcast_mut::()) .expect("components always have a state"); + let context = Context::::new(context); self.on_focus(state, elements, context); } - fn any_blur(&mut self, state: Option<&mut dyn AnyState>, elements: Elements<'_, '_>, context: Context<'_>) { + fn any_blur(&mut self, state: Option<&mut dyn AnyState>, elements: Elements<'_, '_>, context: UntypedContext<'_>) { let state = state .and_then(|s| s.to_any_mut().downcast_mut::()) .expect("components always have a state"); + let context = Context::::new(context); self.on_blur(state, elements, context); } @@ -429,35 +491,44 @@ where &mut self, state: Option<&mut dyn AnyState>, elements: Elements<'_, '_>, - context: Context<'_>, + context: UntypedContext<'_>, dt: Duration, ) { let state = state .and_then(|s| s.to_any_mut().downcast_mut::()) .expect("components always have a state"); + let context = Context::::new(context); self.tick(state, elements, context, dt); } - fn any_resize(&mut self, state: Option<&mut dyn AnyState>, elements: Elements<'_, '_>, context: Context<'_>) { + fn any_resize( + &mut self, + state: Option<&mut dyn AnyState>, + elements: Elements<'_, '_>, + context: UntypedContext<'_>, + ) { let state = state .and_then(|s| s.to_any_mut().downcast_mut::()) .expect("components always have a state"); + let context = Context::::new(context); self.resize(state, elements, context); } - fn any_callback( + fn any_receive( &mut self, state: Option<&mut dyn AnyState>, name: &str, value: CommonVal<'_>, elements: Elements<'_, '_>, - context: Context<'_>, + context: UntypedContext<'_>, ) { let state = state .and_then(|s| s.to_any_mut().downcast_mut::()) .expect("components always have a state"); - self.callback(state, name, value, elements, context); + let context = Context::::new(context); + + self.receive(name, value, state, elements, context); } } diff --git a/anathema-widgets/src/expressions.rs b/anathema-widgets/src/expressions.rs index e76ab5e5..c3338776 100644 --- a/anathema-widgets/src/expressions.rs +++ b/anathema-widgets/src/expressions.rs @@ -371,7 +371,7 @@ impl<'bp> EvalValue<'bp> { match self { EvalValue::Static(p) => (*p).try_into().ok(), EvalValue::Dyn(val) => match val.value::() { - Some(value) => Some(*value), + Some(value) => value.try_as_ref().copied(), None => val.as_state()?.to_common()?.try_into().ok(), }, EvalValue::Index(val, _) => val.load::(), diff --git a/examples/animate.rs b/examples/animate.rs index e79f4446..8f5f4b5c 100644 --- a/examples/animate.rs +++ b/examples/animate.rs @@ -25,7 +25,7 @@ impl Component for C { type Message = (); type State = Num; - fn tick(&mut self, state: &mut Self::State, _: Elements<'_, '_>, context: Context<'_>, dt: Duration) { + fn tick(&mut self, state: &mut Self::State, _: Elements<'_, '_>, context: Context<'_, Self::State>, dt: Duration) { let x = dt.as_millis() as f64; self.val += x / 1000.0 * *state.speed.to_ref(); @@ -33,7 +33,7 @@ impl Component for C { state.x.set(x); } - fn on_key(&mut self, key: KeyEvent, state: &mut Self::State, _: Elements<'_, '_>, _: Context<'_>) { + fn on_key(&mut self, key: KeyEvent, state: &mut Self::State, _: Elements<'_, '_>, _: Context<'_, Self::State>) { if matches!(key.state, KeyState::Press) { match key.code { KeyCode::Char('k') => *state.speed.to_mut() += 0.1, diff --git a/src/lib.rs b/src/lib.rs index 4bacb4ac..ff992f3d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,7 @@ pub use { pub mod prelude { pub use crate::backend::tui::TuiBackend; pub use crate::runtime::Runtime; - pub use crate::templates::{Document, WidgetComponentId}; + pub use crate::templates::{Document, SourceKind, ToSourceKind, WidgetComponentId}; pub use crate::widgets::components::Context; }