mod access;
#[doc(hidden)]
pub mod backend;
mod draw;
mod event;
mod proxy;
mod resource;
use log::debug;
use skia_safe::{
FontMgr, svg,
textlayout::{FontCollection, TypefaceFontProvider},
};
use std::cell::RefCell;
use std::collections::{BinaryHeap, VecDeque};
use std::rc::Rc;
use std::sync::Mutex;
use std::{
any::{Any, TypeId},
sync::Arc,
};
use vizia_id::IdManager;
use vizia_window::WindowDescription;
#[cfg(feature = "clipboard")]
use copypasta::{ClipboardContext, ClipboardProvider, nop_clipboard::NopClipboardContext};
use hashbrown::{HashMap, HashSet, hash_map::Entry};
pub use access::*;
pub use draw::*;
pub use event::*;
pub use proxy::*;
pub use resource::*;
use crate::{
events::{TimedEvent, TimedEventHandle, TimerState, ViewHandler},
model::ModelData,
};
use crate::{binding::BindingHandler, resource::StoredImage};
use crate::{cache::CachedData, resource::ImageOrSvg};
use crate::prelude::*;
use crate::resource::ResourceManager;
use crate::text::TextContext;
use vizia_input::{ImeState, MouseState};
use vizia_storage::{ChildIterator, LayoutTreeIterator};
static DEFAULT_LAYOUT: &str = include_str!("../../resources/themes/default_layout.css");
static DEFAULT_THEME: &str = include_str!("../../resources/themes/default_theme.css");
static MARKDOWN: &str = include_str!("../../resources/themes/markdown.css");
static DEFAULT_TRANSLATION_EN_US: &str =
include_str!("../../resources/translations/en-US/core.ftl");
type Views = HashMap<Entity, Box<dyn ViewHandler>>;
type Models = HashMap<Entity, HashMap<TypeId, Box<dyn ModelData>>>;
type Bindings = HashMap<Entity, Box<dyn BindingHandler>>;
thread_local! {
pub(crate) static SIGNAL_REBUILDS: RefCell<HashSet<Entity>> = RefCell::new(HashSet::new());
}
#[derive(Default, Clone)]
pub struct WindowState {
pub window_description: WindowDescription,
pub scale_factor: f32,
pub needs_relayout: bool,
pub needs_redraw: bool,
pub redraw_list: HashSet<Entity>,
pub dirty_rect: Option<BoundingBox>,
pub owner: Option<Entity>,
pub is_modal: bool,
pub should_close: bool,
pub content: Option<Arc<dyn Fn(&mut Context)>>,
}
pub struct Context {
pub(crate) entity_manager: IdManager<Entity>,
pub(crate) entity_identifiers: HashMap<String, Entity>,
pub tree: Tree<Entity>,
pub(crate) current: Entity,
pub(crate) views: Views,
pub(crate) models: Models,
pub(crate) bindings: Bindings,
pub(crate) event_queue: VecDeque<Event>,
pub(crate) event_schedule: BinaryHeap<TimedEvent>,
pub(crate) next_event_id: usize,
pub(crate) timers: Vec<TimerState>,
pub(crate) running_timers: BinaryHeap<TimerState>,
pub tree_updates: Vec<Option<accesskit::TreeUpdate>>,
pub(crate) listeners:
HashMap<Entity, Box<dyn Fn(&mut dyn ViewHandler, &mut EventContext, &mut Event)>>,
pub(crate) global_listeners: Vec<Box<dyn Fn(&mut EventContext, &mut Event)>>,
pub(crate) style: Style,
pub(crate) cache: CachedData,
pub windows: HashMap<Entity, WindowState>,
pub mouse: MouseState<Entity>,
pub(crate) modifiers: Modifiers,
pub(crate) captured: Entity,
pub(crate) triggered: Entity,
pub(crate) hovered: Entity,
pub(crate) focused: Entity,
pub(crate) focus_stack: Vec<Entity>,
pub(crate) cursor_icon_locked: bool,
pub(crate) resource_manager: ResourceManager,
pub text_context: TextContext,
pub(crate) event_proxy: Option<Box<dyn EventProxy>>,
#[cfg(feature = "clipboard")]
pub(crate) clipboard: Box<dyn ClipboardProvider>,
pub(crate) click_time: Instant,
pub(crate) clicks: usize,
pub(crate) click_pos: (f32, f32),
pub(crate) click_button: MouseButton,
pub ignore_default_theme: bool,
built_in_translations_added: bool,
pub window_has_focus: bool,
pub ime_state: ImeState,
pub(crate) drop_data: Option<DropData>,
}
impl Default for Context {
fn default() -> Self {
Context::new()
}
}
impl Context {
pub fn new() -> Self {
let mut cache = CachedData::default();
cache.add(Entity::root());
let mut result = Self {
entity_manager: IdManager::new(),
entity_identifiers: HashMap::new(),
tree: Tree::new(),
current: Entity::root(),
views: HashMap::default(),
models: HashMap::default(),
bindings: HashMap::default(),
style: Style::default(),
cache,
windows: HashMap::new(),
event_queue: VecDeque::new(),
event_schedule: BinaryHeap::new(),
next_event_id: 0,
timers: Vec::new(),
running_timers: BinaryHeap::new(),
tree_updates: Vec::new(),
listeners: HashMap::default(),
global_listeners: Vec::new(),
mouse: MouseState::default(),
modifiers: Modifiers::empty(),
captured: Entity::null(),
triggered: Entity::null(),
hovered: Entity::root(),
focused: Entity::root(),
focus_stack: Vec::new(),
cursor_icon_locked: false,
resource_manager: ResourceManager::new(),
text_context: {
let mut font_collection = FontCollection::new();
let default_font_manager = FontMgr::default();
let asset_provider = TypefaceFontProvider::new();
font_collection.set_default_font_manager(default_font_manager.clone(), None);
let asset_font_manager: FontMgr = asset_provider.clone().into();
font_collection.set_asset_font_manager(asset_font_manager);
TextContext {
font_collection,
default_font_manager,
asset_provider,
text_bounds: Default::default(),
text_paragraphs: Default::default(),
}
},
event_proxy: None,
#[cfg(feature = "clipboard")]
clipboard: {
if let Ok(context) = ClipboardContext::new() {
Box::new(context)
} else {
Box::new(NopClipboardContext::new().unwrap())
}
},
click_time: Instant::now(),
clicks: 0,
click_pos: (0.0, 0.0),
click_button: MouseButton::Left,
ignore_default_theme: false,
built_in_translations_added: false,
window_has_focus: true,
ime_state: Default::default(),
drop_data: None,
};
result.tree.set_window(Entity::root(), true);
result.style.needs_restyle(Entity::root());
result.style.needs_relayout();
result.style.needs_retransform(Entity::root());
result.style.needs_reclip(Entity::root());
result.needs_redraw(Entity::root());
result.style.dpi_factor = 1.0;
Environment::new(&mut result).build(&mut result);
result.entity_manager.create();
result.style.role.insert(Entity::root(), Role::Window);
result
}
pub fn current(&self) -> Entity {
self.current
}
pub fn with_current<T>(&mut self, current: Entity, f: impl FnOnce(&mut Context) -> T) -> T {
let previous = self.current;
self.current = current;
let ret = f(self);
self.current = previous;
ret
}
pub fn environment(&self) -> &Environment {
self.data::<Environment>()
}
pub fn parent_window(&self) -> Entity {
self.tree.get_parent_window(self.current).unwrap_or(Entity::root())
}
pub fn scale_factor(&self) -> f32 {
self.style.dpi_factor as f32
}
pub fn needs_redraw(&mut self, entity: Entity) {
if self.entity_manager.is_alive(entity) {
let window = if self.tree.is_window(entity) {
entity
} else {
self.tree.get_parent_window(entity).unwrap_or(Entity::root())
};
if let Some(window_state) = self.windows.get_mut(&window) {
window_state.redraw_list.insert(entity);
}
}
}
pub fn needs_restyle(&mut self, entity: Entity) {
if entity == Entity::null() || self.style.restyle.contains(&entity) {
return;
}
self.style.restyle.insert(entity);
let iter = if let Some(parent) = self.tree.get_layout_parent(entity) {
LayoutTreeIterator::subtree(&self.tree, parent)
} else {
LayoutTreeIterator::subtree(&self.tree, entity)
};
for descendant in iter {
self.style.restyle.insert(descendant);
}
}
pub fn needs_retransform(&mut self, entity: Entity) {
self.style.needs_retransform(entity);
let iter = LayoutTreeIterator::subtree(&self.tree, entity);
for descendant in iter {
self.style.needs_retransform(descendant);
}
}
pub fn needs_reclip(&mut self, entity: Entity) {
self.style.needs_reclip(entity);
let iter = LayoutTreeIterator::subtree(&self.tree, entity);
for descendant in iter {
self.style.needs_reclip(descendant);
}
}
pub fn needs_relayout(&mut self) {
self.style.needs_relayout();
}
pub(crate) fn set_system_flags(&mut self, entity: Entity, system_flags: SystemFlags) {
if system_flags.contains(SystemFlags::RELAYOUT) {
self.needs_relayout();
}
if system_flags.contains(SystemFlags::RESTYLE) {
self.needs_restyle(entity);
}
if system_flags.contains(SystemFlags::REDRAW) {
self.needs_redraw(entity);
}
if system_flags.contains(SystemFlags::REFLOW) {
self.style.needs_text_update(entity);
}
if system_flags.contains(SystemFlags::RETRANSFORM) {
self.needs_retransform(entity);
}
if system_flags.contains(SystemFlags::RECLIP) {
self.needs_reclip(entity);
}
if system_flags.contains(SystemFlags::REACCESS) {
self.style.needs_access_update(entity);
}
}
pub(crate) fn set_focus_pseudo_classes(
&mut self,
focused: Entity,
enabled: bool,
focus_visible: bool,
) {
if enabled {
debug!(
"Focus changed to {:?} parent: {:?}, view: {}, posx: {}, posy: {} width: {} height: {}",
focused,
self.tree.get_parent(focused),
self.views
.get(&focused)
.map_or("<None>", |view| view.element().unwrap_or("<Unnamed>")),
self.cache.get_posx(focused),
self.cache.get_posy(focused),
self.cache.get_width(focused),
self.cache.get_height(focused),
);
}
if let Some(pseudo_classes) = self.style.pseudo_classes.get_mut(focused) {
pseudo_classes.set(PseudoClassFlags::FOCUS, enabled);
if !enabled || focus_visible {
pseudo_classes.set(PseudoClassFlags::FOCUS_VISIBLE, enabled);
self.style.needs_access_update(focused);
self.needs_restyle(focused);
}
}
let ancestors = focused.parent_iter(&self.tree).collect::<Vec<_>>();
for entity in ancestors {
if let Some(pseudo_classes) = self.style.pseudo_classes.get_mut(entity) {
pseudo_classes.set(PseudoClassFlags::FOCUS_WITHIN, enabled);
}
self.needs_restyle(entity);
}
}
pub fn focus_with_visibility(&mut self, focus_visible: bool) {
let old_focus = self.focused;
let new_focus = self.current;
self.set_focus_pseudo_classes(old_focus, false, focus_visible);
if self.current != self.focused {
self.emit_to(old_focus, WindowEvent::FocusOut);
self.emit_to(new_focus, WindowEvent::FocusIn);
self.focused = self.current;
}
self.set_focus_pseudo_classes(new_focus, true, focus_visible);
self.emit_custom(Event::new(WindowEvent::FocusVisibility(focus_visible)).target(old_focus));
self.emit_custom(Event::new(WindowEvent::FocusVisibility(focus_visible)).target(new_focus));
self.needs_restyle(self.focused);
self.needs_restyle(self.current);
self.style.needs_access_update(self.focused);
self.style.needs_access_update(self.current);
}
pub fn focus(&mut self) {
let focused = self.focused;
let old_focus_visible = self
.style
.pseudo_classes
.get_mut(focused)
.filter(|class| class.contains(PseudoClassFlags::FOCUS_VISIBLE))
.is_some();
self.focus_with_visibility(old_focus_visible)
}
pub(crate) fn remove_children(&mut self, entity: Entity) {
let child_iter = ChildIterator::new(&self.tree, entity);
let children = child_iter.collect::<Vec<_>>();
for child in children.into_iter() {
self.remove(child);
}
}
pub fn remove(&mut self, entity: Entity) {
let delete_list = entity.branch_iter(&self.tree).collect::<Vec<_>>();
if !delete_list.is_empty() {
self.style.needs_restyle(self.current);
self.style.needs_relayout();
self.needs_redraw(self.current);
}
for entity in delete_list.iter().rev() {
if let Some(mut view) = self.views.remove(entity) {
view.event(
&mut EventContext::new_with_current(self, *entity),
&mut Event::new(WindowEvent::Destroyed).direct(*entity),
);
self.views.insert(*entity, view);
}
if let Some(binding) = self.bindings.remove(entity) {
binding.remove(self);
self.bindings.insert(*entity, binding);
}
for image in self.resource_manager.images.values_mut() {
image.observers.remove(entity);
}
if let Some(identifier) = self.style.ids.get(*entity) {
self.entity_identifiers.remove(identifier);
}
if let Some(index) = self.focus_stack.iter().position(|r| r == entity) {
self.focus_stack.remove(index);
}
if self.focused == *entity {
if let Some(new_focus) = self.focus_stack.pop() {
self.with_current(new_focus, |cx| cx.focus());
} else {
self.with_current(Entity::root(), |cx| cx.focus());
}
}
if self.captured == *entity {
self.captured = Entity::null();
}
if let Some(parent) = self.tree.get_layout_parent(*entity) {
self.style.needs_access_update(parent);
}
let mut stopped_timers = Vec::new();
for timer in self.running_timers.iter() {
if timer.entity == *entity {
stopped_timers.push(timer.id);
}
}
for timer in stopped_timers {
self.stop_timer(timer);
}
let window_entity = self.tree.get_parent_window(*entity).unwrap_or(Entity::root());
if !self.tree.is_window(*entity) {
if let Some(draw_bounds) = self.cache.draw_bounds.get(*entity) {
if let Some(dirty_rect) =
&mut self.windows.get_mut(&window_entity).unwrap().dirty_rect
{
*dirty_rect = dirty_rect.union(draw_bounds);
} else {
self.windows.get_mut(&window_entity).unwrap().dirty_rect =
Some(*draw_bounds);
}
}
}
self.windows.get_mut(&window_entity).unwrap().redraw_list.remove(entity);
if self.windows.contains_key(entity) {
self.windows.remove(entity);
}
self.tree.remove(*entity).expect("");
self.cache.remove(*entity);
self.style.remove(*entity);
self.models.remove(entity);
self.views.remove(entity);
self.text_context.text_bounds.remove(*entity);
self.text_context.text_paragraphs.remove(*entity);
self.entity_manager.destroy(*entity);
}
}
pub fn toggle_class(&mut self, name: &str, applied: impl Res<bool>) {
let name = name.to_owned();
let entity = self.current();
let current = self.current();
self.with_current(current, |cx| {
applied.set_or_bind(cx, move |cx, applied| {
let applied = applied.get_value(cx);
if let Some(class_list) = cx.style.classes.get_mut(entity) {
if applied {
class_list.insert(name.clone());
} else {
class_list.remove(&name);
}
}
cx.needs_restyle(entity);
});
});
}
pub fn add_listener<F, W>(&mut self, listener: F)
where
W: View,
F: 'static + Fn(&mut W, &mut EventContext, &mut Event),
{
self.listeners.insert(
self.current,
Box::new(move |event_handler, context, event| {
if let Some(widget) = event_handler.downcast_mut::<W>() {
(listener)(widget, context, event);
}
}),
);
}
pub fn add_global_listener<F>(&mut self, listener: F)
where
F: 'static + Fn(&mut EventContext, &mut Event),
{
self.global_listeners.push(Box::new(listener));
}
pub fn add_font_mem(&mut self, data: impl AsRef<[u8]>) {
self.text_context.asset_provider.register_typeface(
self.text_context.default_font_manager.new_from_data(data.as_ref(), None).unwrap(),
None,
);
}
pub fn add_stylesheet(&mut self, style: impl IntoCssStr) -> Result<(), std::io::Error> {
self.resource_manager.styles.push(Box::new(style));
EventContext::new(self).reload_styles().expect("Failed to reload styles");
Ok(())
}
pub fn add_built_in_styles(&mut self) {
self.add_built_in_translations();
let mut user_styles = Vec::new();
if self.resource_manager.styles.len() >= 3 {
user_styles = self.resource_manager.styles.drain(3..).collect::<Vec<_>>();
}
self.resource_manager.styles.clear();
self.add_stylesheet(DEFAULT_LAYOUT).unwrap();
self.add_stylesheet(MARKDOWN).unwrap();
if !self.ignore_default_theme {
self.add_stylesheet(DEFAULT_THEME).unwrap();
let environment = self.data::<Environment>();
let theme_mode = environment.effective_theme();
let direction = environment.direction.get();
self.with_current(Entity::root(), |cx| {
let cx = &mut EventContext::new(cx);
cx.toggle_class("dark", theme_mode == ThemeMode::DarkMode);
cx.toggle_class("rtl", direction == Direction::RightToLeft);
})
} else {
self.add_stylesheet("").unwrap();
}
self.resource_manager.styles.extend(user_styles);
EventContext::new(self).reload_styles().unwrap();
}
pub fn add_built_in_translations(&mut self) {
if self.built_in_translations_added {
return;
}
self.add_translation("en-US".parse().unwrap(), DEFAULT_TRANSLATION_EN_US)
.expect("Failed to load built-in en-US translation resources");
self.built_in_translations_added = true;
}
pub fn add_animation(&mut self, animation: AnimationBuilder) -> Animation {
self.style.add_animation(animation)
}
pub fn set_image_loader<F: 'static + Fn(&mut ResourceContext, &str)>(&mut self, loader: F) {
self.resource_manager.image_loader = Some(Box::new(loader));
}
pub fn add_translation(
&mut self,
lang: LanguageIdentifier,
ftl: impl ToString,
) -> Result<(), crate::resource::TranslationError> {
self.resource_manager.add_translation(lang, ftl.to_string())
}
pub fn add_timer(
&mut self,
interval: Duration,
duration: Option<Duration>,
callback: impl Fn(&mut EventContext, TimerAction) + 'static,
) -> Timer {
let id = Timer(self.timers.len());
self.timers.push(TimerState {
entity: Entity::root(),
id,
time: Instant::now(),
interval,
duration,
start_time: Instant::now(),
callback: Rc::new(callback),
ticking: false,
stopping: false,
});
id
}
pub fn start_timer(&mut self, timer: Timer) {
let current = self.current;
if !self.timer_is_running(timer) {
let timer_state = self.timers[timer.0].clone();
self.running_timers.push(timer_state);
}
self.modify_timer(timer, |timer_state| {
let now = Instant::now();
timer_state.start_time = now;
timer_state.time = now;
timer_state.entity = current;
timer_state.ticking = false;
timer_state.stopping = false;
});
}
pub fn modify_timer(&mut self, timer: Timer, timer_function: impl Fn(&mut TimerState)) {
while let Some(next_timer_state) = self.running_timers.peek() {
if next_timer_state.id == timer {
let mut timer_state = self.running_timers.pop().unwrap();
(timer_function)(&mut timer_state);
self.running_timers.push(timer_state);
return;
}
}
for pending_timer in self.timers.iter_mut() {
if pending_timer.id == timer {
(timer_function)(pending_timer);
}
}
}
pub fn timer_is_running(&mut self, timer: Timer) -> bool {
for timer_state in self.running_timers.iter() {
if timer_state.id == timer {
return true;
}
}
false
}
pub fn stop_timer(&mut self, timer: Timer) {
let mut running_timers = self.running_timers.clone();
for timer_state in running_timers.iter() {
if timer_state.id == timer {
(timer_state.callback)(
&mut EventContext::new_with_current(self, timer_state.entity),
TimerAction::Stop,
);
}
}
self.running_timers =
running_timers.drain().filter(|timer_state| timer_state.id != timer).collect();
}
pub(crate) fn tick_timers(&mut self) {
let now = Instant::now();
while let Some(next_timer_state) = self.running_timers.peek() {
if next_timer_state.time <= now {
let mut timer_state = self.running_timers.pop().unwrap();
if timer_state.end_time().unwrap_or_else(|| now + Duration::from_secs(1)) >= now {
if !timer_state.ticking {
(timer_state.callback)(
&mut EventContext::new_with_current(self, timer_state.entity),
TimerAction::Start,
);
timer_state.ticking = true;
} else {
(timer_state.callback)(
&mut EventContext::new_with_current(self, timer_state.entity),
TimerAction::Tick(now - timer_state.time),
);
}
timer_state.time = now + timer_state.interval - (now - timer_state.time);
self.running_timers.push(timer_state);
} else {
(timer_state.callback)(
&mut EventContext::new_with_current(self, timer_state.entity),
TimerAction::Stop,
);
}
} else {
break;
}
}
}
pub fn load_image(&mut self, path: &str, data: &'static [u8], policy: ImageRetentionPolicy) {
let id = if let Some(image_id) = self.resource_manager.image_ids.get(path) {
*image_id
} else {
let id = self.resource_manager.image_id_manager.create();
self.resource_manager.image_ids.insert(path.to_owned(), id);
id
};
if let Some(image) =
skia_safe::Image::from_encoded(unsafe { skia_safe::Data::new_bytes(data) })
{
match self.resource_manager.images.entry(id) {
Entry::Occupied(mut occ) => {
occ.get_mut().image = ImageOrSvg::Image(image);
occ.get_mut().dirty = true;
occ.get_mut().retention_policy = policy;
}
Entry::Vacant(vac) => {
vac.insert(StoredImage {
image: ImageOrSvg::Image(image),
retention_policy: policy,
used: true,
dirty: false,
observers: HashSet::new(),
});
}
}
self.style.needs_relayout();
}
}
pub fn load_svg(&mut self, path: &str, data: &[u8], policy: ImageRetentionPolicy) -> ImageId {
let id = if let Some(image_id) = self.resource_manager.image_ids.get(path) {
return *image_id;
} else {
let id = self.resource_manager.image_id_manager.create();
self.resource_manager.image_ids.insert(path.to_owned(), id);
id
};
if let Ok(svg) = svg::Dom::from_bytes(data, self.text_context.default_font_manager.clone())
{
match self.resource_manager.images.entry(id) {
Entry::Occupied(mut occ) => {
occ.get_mut().image = ImageOrSvg::Svg(svg);
occ.get_mut().dirty = true;
occ.get_mut().retention_policy = policy;
}
Entry::Vacant(vac) => {
vac.insert(StoredImage {
image: ImageOrSvg::Svg(svg),
retention_policy: policy,
used: true,
dirty: false,
observers: HashSet::new(),
});
}
}
self.style.needs_relayout();
}
id
}
pub fn spawn<F>(&self, target: F)
where
F: 'static + Send + FnOnce(&mut ContextProxy),
{
let mut cxp = ContextProxy {
current: self.current,
event_proxy: self.event_proxy.as_ref().map(|p| p.make_clone()),
};
std::thread::spawn(move || target(&mut cxp));
}
pub fn get_proxy(&self) -> ContextProxy {
ContextProxy {
current: self.current,
event_proxy: self.event_proxy.as_ref().map(|p| p.make_clone()),
}
}
pub(crate) fn resolve_entity_identifier(&self, identity: &str) -> Option<Entity> {
self.entity_identifiers.get(identity).cloned()
}
pub fn set_ime_state(&mut self, new_state: ImeState) {
self.ime_state = new_state;
}
}
pub(crate) enum InternalEvent {
Redraw,
LoadImage { path: String, image: Mutex<Option<skia_safe::Image>>, policy: ImageRetentionPolicy },
}
pub struct LocalizationContext<'a> {
pub(crate) current: Entity,
pub(crate) resource_manager: &'a ResourceManager,
pub(crate) models: &'a Models,
pub(crate) views: &'a Views,
pub(crate) tree: &'a Tree<Entity>,
}
impl<'a> LocalizationContext<'a> {
pub(crate) fn from_context(cx: &'a Context) -> Self {
Self {
current: cx.current,
resource_manager: &cx.resource_manager,
models: &cx.models,
views: &cx.views,
tree: &cx.tree,
}
}
pub(crate) fn from_event_context(cx: &'a EventContext) -> Self {
Self {
current: cx.current,
resource_manager: cx.resource_manager,
models: cx.models,
views: cx.views,
tree: cx.tree,
}
}
pub(crate) fn environment(&self) -> &Environment {
self.data::<Environment>()
}
}
pub trait DataContext {
fn try_data<T: 'static>(&self) -> Option<&T>;
fn data<T: 'static>(&self) -> &T {
self.try_data::<T>().expect("data not found in context")
}
fn localization_context(&self) -> Option<LocalizationContext<'_>> {
None
}
}
pub trait EmitContext {
fn emit<M: Any + Send>(&mut self, message: M);
fn emit_to<M: Any + Send>(&mut self, target: Entity, message: M);
fn emit_custom(&mut self, event: Event);
fn schedule_emit<M: Any + Send>(&mut self, message: M, at: Instant) -> TimedEventHandle;
fn schedule_emit_to<M: Any + Send>(
&mut self,
target: Entity,
message: M,
at: Instant,
) -> TimedEventHandle;
fn schedule_emit_custom(&mut self, event: Event, at: Instant) -> TimedEventHandle;
fn cancel_scheduled(&mut self, handle: TimedEventHandle);
}
impl DataContext for Context {
fn try_data<T: 'static>(&self) -> Option<&T> {
if let Some(t) = <dyn Any>::downcast_ref::<T>(&()) {
return Some(t);
}
for entity in self.current.parent_iter(&self.tree) {
if let Some(models) = self.models.get(&entity) {
if let Some(model) = models.get(&TypeId::of::<T>()) {
return model.downcast_ref::<T>();
}
}
if let Some(view_handler) = self.views.get(&entity) {
if let Some(data) = view_handler.downcast_ref::<T>() {
return Some(data);
}
}
}
None
}
fn localization_context(&self) -> Option<LocalizationContext<'_>> {
Some(LocalizationContext::from_context(self))
}
}
impl DataContext for LocalizationContext<'_> {
fn try_data<T: 'static>(&self) -> Option<&T> {
if let Some(t) = <dyn Any>::downcast_ref::<T>(&()) {
return Some(t);
}
for entity in self.current.parent_iter(self.tree) {
if let Some(models) = self.models.get(&entity) {
if let Some(model) = models.get(&TypeId::of::<T>()) {
return model.downcast_ref::<T>();
}
}
if let Some(view_handler) = self.views.get(&entity) {
if let Some(data) = view_handler.downcast_ref::<T>() {
return Some(data);
}
}
}
None
}
}
impl EmitContext for Context {
fn emit<M: Any + Send>(&mut self, message: M) {
self.event_queue.push_back(
Event::new(message)
.target(self.current)
.origin(self.current)
.propagate(Propagation::Up),
);
}
fn emit_to<M: Any + Send>(&mut self, target: Entity, message: M) {
self.event_queue.push_back(
Event::new(message).target(target).origin(self.current).propagate(Propagation::Direct),
);
}
fn emit_custom(&mut self, event: Event) {
self.event_queue.push_back(event);
}
fn schedule_emit<M: Any + Send>(&mut self, message: M, at: Instant) -> TimedEventHandle {
self.schedule_emit_custom(
Event::new(message)
.target(self.current)
.origin(self.current)
.propagate(Propagation::Up),
at,
)
}
fn schedule_emit_to<M: Any + Send>(
&mut self,
target: Entity,
message: M,
at: Instant,
) -> TimedEventHandle {
self.schedule_emit_custom(
Event::new(message).target(target).origin(self.current).propagate(Propagation::Direct),
at,
)
}
fn schedule_emit_custom(&mut self, event: Event, at: Instant) -> TimedEventHandle {
let handle = TimedEventHandle(self.next_event_id);
self.event_schedule.push(TimedEvent { event, time: at, ident: handle });
self.next_event_id += 1;
handle
}
fn cancel_scheduled(&mut self, handle: TimedEventHandle) {
self.event_schedule =
self.event_schedule.drain().filter(|item| item.ident != handle).collect();
}
}