Skip to main content

vizia_core/context/
mod.rs

1//! Context types for retained state, used during view building, event handling, and drawing.
2
3mod access;
4#[doc(hidden)]
5pub mod backend;
6mod draw;
7mod event;
8mod proxy;
9mod resource;
10#[cfg(feature = "tokio")]
11mod task;
12
13use log::debug;
14use skia_safe::{
15    FontMgr, svg,
16    textlayout::{FontCollection, TypefaceFontProvider},
17};
18use std::cell::RefCell;
19use std::collections::{BinaryHeap, VecDeque};
20use std::rc::Rc;
21use std::sync::Mutex;
22use std::sync::atomic::{AtomicU64, Ordering};
23use std::{
24    any::{Any, TypeId},
25    sync::Arc,
26};
27use vizia_id::IdManager;
28use vizia_window::WindowDescription;
29
30#[cfg(all(
31    feature = "clipboard",
32    not(all(
33        any(
34            target_os = "linux",
35            target_os = "dragonfly",
36            target_os = "freebsd",
37            target_os = "netbsd",
38            target_os = "openbsd"
39        ),
40        feature = "wayland",
41    ))
42))]
43use copypasta::ClipboardContext;
44#[cfg(feature = "clipboard")]
45use copypasta::{ClipboardProvider, nop_clipboard::NopClipboardContext};
46use hashbrown::{HashMap, HashSet, hash_map::Entry};
47
48pub use access::*;
49pub use draw::*;
50pub use event::*;
51pub use proxy::*;
52pub use resource::*;
53#[cfg(feature = "tokio")]
54pub use task::*;
55
56use crate::{
57    events::{TimedEvent, TimedEventHandle, TimerState, ViewHandler},
58    model::ModelData,
59};
60
61use crate::{binding::BindingHandler, resource::StoredImage};
62use crate::{cache::CachedData, resource::ImageOrSvg};
63
64use crate::prelude::*;
65use crate::resource::ResourceManager;
66use crate::text::TextContext;
67use vizia_input::{ImeState, MouseState};
68use vizia_storage::{ChildIterator, LayoutTreeIterator};
69
70#[cfg(feature = "tokio")]
71pub(crate) type TaskRuntime = Arc<tokio::runtime::Runtime>;
72
73static DEFAULT_LAYOUT: &str = include_str!("../../resources/themes/default_layout.css");
74static DEFAULT_THEME: &str = include_str!("../../resources/themes/default_theme.css");
75static MARKDOWN: &str = include_str!("../../resources/themes/markdown.css");
76static DEFAULT_TRANSLATION_EN_US: &str =
77    include_str!("../../resources/translations/en-US/core.ftl");
78
79type Views = HashMap<Entity, Box<dyn ViewHandler>>;
80type Models = HashMap<Entity, HashMap<TypeId, Box<dyn ModelData>>>;
81type Bindings = HashMap<Entity, Box<dyn BindingHandler>>;
82
83static NEXT_CONTEXT_ID: AtomicU64 = AtomicU64::new(1);
84
85#[derive(Clone, Copy, PartialEq, Eq, Hash)]
86pub(crate) struct SignalRebuild {
87    pub(crate) context_id: u64,
88    pub(crate) entity: Entity,
89}
90
91#[cfg(feature = "clipboard")]
92fn default_clipboard_provider() -> Box<dyn ClipboardProvider> {
93    #[cfg(all(
94        any(
95            target_os = "linux",
96            target_os = "dragonfly",
97            target_os = "freebsd",
98            target_os = "netbsd",
99            target_os = "openbsd"
100        ),
101        feature = "wayland"
102    ))]
103    {
104        Box::new(NopClipboardContext::new().unwrap())
105    }
106
107    #[cfg(not(all(
108        any(
109            target_os = "linux",
110            target_os = "dragonfly",
111            target_os = "freebsd",
112            target_os = "netbsd",
113            target_os = "openbsd"
114        ),
115        feature = "wayland",
116    )))]
117    {
118        if let Ok(context) = ClipboardContext::new() {
119            Box::new(context)
120        } else {
121            Box::new(NopClipboardContext::new().unwrap())
122        }
123    }
124}
125
126thread_local! {
127    /// Entities for `Binding` views that need to be rebuilt because a reactive signal changed.
128    /// Signal effects push to this set; the binding system drains matching context entries each frame.
129    pub(crate) static SIGNAL_REBUILDS: RefCell<HashSet<SignalRebuild>> = RefCell::new(HashSet::new());
130}
131
132#[derive(Default, Clone)]
133pub struct WindowState {
134    pub window_description: WindowDescription,
135    pub scale_factor: f32,
136    pub needs_relayout: bool,
137    pub needs_redraw: bool,
138    pub redraw_list: HashSet<Entity>,
139    pub dirty_rect: Option<BoundingBox>,
140    pub owner: Option<Entity>,
141    pub is_modal: bool,
142    pub should_close: bool,
143    pub content: Option<Arc<dyn Fn(&mut Context)>>,
144}
145
146/// The main storage and control object for a Vizia application.
147pub struct Context {
148    pub(crate) context_id: u64,
149    pub(crate) entity_manager: IdManager<Entity>,
150    pub(crate) entity_identifiers: HashMap<String, Entity>,
151    pub tree: Tree<Entity>,
152    pub(crate) current: Entity,
153    pub(crate) views: Views,
154    pub(crate) models: Models,
155    pub(crate) bindings: Bindings,
156    pub(crate) event_queue: VecDeque<Event>,
157    pub(crate) event_schedule: BinaryHeap<TimedEvent>,
158    pub(crate) next_event_id: usize,
159    pub(crate) timers: Vec<TimerState>,
160    pub(crate) running_timers: BinaryHeap<TimerState>,
161    pub tree_updates: Vec<Option<accesskit::TreeUpdate>>,
162    pub(crate) listeners:
163        HashMap<Entity, Box<dyn Fn(&mut dyn ViewHandler, &mut EventContext, &mut Event)>>,
164    pub(crate) global_listeners: Vec<Box<dyn Fn(&mut EventContext, &mut Event)>>,
165    pub(crate) style: Style,
166    pub(crate) cache: CachedData,
167    pub windows: HashMap<Entity, WindowState>,
168
169    pub mouse: MouseState<Entity>,
170    pub(crate) modifiers: Modifiers,
171
172    pub(crate) captured: Entity,
173    pub(crate) triggered: Entity,
174    pub(crate) hovered: Entity,
175    pub(crate) drag_hovered: Entity,
176    pub(crate) focused: Entity,
177    pub(crate) focus_stack: Vec<Entity>,
178    pub(crate) cursor_icon_locked: bool,
179
180    pub(crate) resource_manager: ResourceManager,
181
182    pub text_context: TextContext,
183
184    #[cfg(feature = "tokio")]
185    pub(crate) task_runtime: TaskRuntime,
186    #[cfg(feature = "tokio")]
187    pub(crate) named_tasks: NamedTaskMap,
188
189    pub(crate) event_proxy: Option<Box<dyn EventProxy>>,
190
191    #[cfg(feature = "clipboard")]
192    pub(crate) clipboards: HashMap<Entity, Box<dyn ClipboardProvider>>,
193
194    pub(crate) click_time: Instant,
195    pub(crate) clicks: usize,
196    pub(crate) click_pos: (f32, f32),
197    pub(crate) click_button: MouseButton,
198
199    pub ignore_default_theme: bool,
200    built_in_translations_added: bool,
201    built_in_styles_added: bool,
202    pub window_has_focus: bool,
203    pub ime_state: ImeState,
204
205    pub(crate) drop_data: Option<DropData>,
206    pub(crate) active_drag_view: Option<Entity>,
207}
208
209impl Default for Context {
210    fn default() -> Self {
211        Context::new()
212    }
213}
214
215impl Context {
216    /// Creates a new context.
217    pub fn new() -> Self {
218        let mut cache = CachedData::default();
219        cache.add(Entity::root());
220
221        let mut result = Self {
222            context_id: NEXT_CONTEXT_ID.fetch_add(1, Ordering::Relaxed),
223            entity_manager: IdManager::new(),
224            entity_identifiers: HashMap::new(),
225            tree: Tree::new(),
226            current: Entity::root(),
227            views: HashMap::default(),
228            models: HashMap::default(),
229            bindings: HashMap::default(),
230            style: Style::default(),
231            cache,
232            windows: HashMap::new(),
233            event_queue: VecDeque::new(),
234            event_schedule: BinaryHeap::new(),
235            next_event_id: 0,
236            timers: Vec::new(),
237            running_timers: BinaryHeap::new(),
238            tree_updates: Vec::new(),
239            listeners: HashMap::default(),
240            global_listeners: Vec::new(),
241            mouse: MouseState::default(),
242            modifiers: Modifiers::empty(),
243            captured: Entity::null(),
244            triggered: Entity::null(),
245            hovered: Entity::root(),
246            drag_hovered: Entity::null(),
247            focused: Entity::root(),
248            focus_stack: Vec::new(),
249            cursor_icon_locked: false,
250            resource_manager: ResourceManager::new(),
251            text_context: {
252                let mut font_collection = FontCollection::new();
253
254                let default_font_manager = FontMgr::default();
255
256                let asset_provider = TypefaceFontProvider::new();
257
258                font_collection.set_default_font_manager(default_font_manager.clone(), None);
259                let asset_font_manager: FontMgr = asset_provider.clone().into();
260                font_collection.set_asset_font_manager(asset_font_manager);
261
262                TextContext {
263                    font_collection,
264                    default_font_manager,
265                    asset_provider,
266                    text_bounds: Default::default(),
267                    text_paragraphs: Default::default(),
268                }
269            },
270            #[cfg(feature = "tokio")]
271            task_runtime: Self::new_task_runtime(),
272            #[cfg(feature = "tokio")]
273            named_tasks: new_named_task_map(),
274
275            event_proxy: None,
276
277            #[cfg(feature = "clipboard")]
278            clipboards: HashMap::new(),
279            click_time: Instant::now(),
280            clicks: 0,
281            click_pos: (0.0, 0.0),
282            click_button: MouseButton::Left,
283
284            ignore_default_theme: false,
285            built_in_translations_added: false,
286            built_in_styles_added: false,
287            window_has_focus: true,
288
289            ime_state: Default::default(),
290
291            drop_data: None,
292            active_drag_view: None,
293        };
294
295        result.tree.set_window(Entity::root(), true);
296
297        result.style.needs_restyle(Entity::root());
298        result.style.needs_relayout(Entity::root());
299        result.style.needs_retransform(Entity::root());
300        result.style.needs_reclip(Entity::root());
301        result.needs_redraw(Entity::root());
302
303        // Set the default DPI factor to 1.0.
304        result.style.dpi_factor = 1.0;
305
306        // Build the environment model at the root.
307        Environment::new(&mut result).build(&mut result);
308
309        result.entity_manager.create();
310
311        result.style.role.insert(Entity::root(), Role::Window);
312
313        result
314    }
315
316    #[cfg(feature = "tokio")]
317    fn new_task_runtime() -> TaskRuntime {
318        Arc::new(
319            tokio::runtime::Builder::new_multi_thread()
320                .enable_all()
321                .build()
322                .expect("failed to build context task runtime"),
323        )
324    }
325
326    /// The "current" entity, generally the entity which is currently being built or the entity
327    /// which is currently having an event dispatched to it.
328    pub fn current(&self) -> Entity {
329        self.current
330    }
331
332    /// Makes the above black magic more explicit
333    pub fn with_current<T>(&mut self, current: Entity, f: impl FnOnce(&mut Context) -> T) -> T {
334        let previous = self.current;
335        self.current = current;
336        let ret = f(self);
337        self.current = previous;
338        ret
339    }
340
341    /// Returns a reference to the [Environment] model.
342    pub fn environment(&self) -> &Environment {
343        self.data::<Environment>()
344    }
345
346    /// Returns the entity id of the  parent window to the current view.
347    pub fn parent_window(&self) -> Entity {
348        self.tree.get_parent_window(self.current).unwrap_or(Entity::root())
349    }
350
351    /// Returns the scale factor of the display.
352    pub fn scale_factor(&self) -> f32 {
353        self.style.dpi_factor as f32
354    }
355
356    /// Mark the application as needing to rerun the draw method
357    pub fn needs_redraw(&mut self, entity: Entity) {
358        if self.entity_manager.is_alive(entity) {
359            // If a child window needs redrawing, add itself to the redraw list.
360            // This ensures that the entire window is redrawn: https://github.com/vizia/vizia/issues/580
361            let window = if self.tree.is_window(entity) {
362                entity
363            } else {
364                self.tree.get_parent_window(entity).unwrap_or(Entity::root())
365            };
366            if let Some(window_state) = self.windows.get_mut(&window) {
367                window_state.redraw_list.insert(entity);
368            }
369        }
370    }
371
372    /// Mark the application as needing to recompute view styles
373    pub fn needs_restyle(&mut self, entity: Entity) {
374        if entity == Entity::null() || self.style.restyle.contains(&entity) {
375            return;
376        }
377        self.style.restyle.insert(entity);
378        let iter = if let Some(parent) = self.tree.get_layout_parent(entity) {
379            LayoutTreeIterator::subtree(&self.tree, parent)
380        } else {
381            LayoutTreeIterator::subtree(&self.tree, entity)
382        };
383
384        for descendant in iter {
385            self.style.restyle.insert(descendant);
386        }
387        // self.style.needs_restyle();
388    }
389
390    pub fn needs_retransform(&mut self, entity: Entity) {
391        self.style.needs_retransform(entity);
392        let iter = LayoutTreeIterator::subtree(&self.tree, entity);
393        for descendant in iter {
394            self.style.needs_retransform(descendant);
395        }
396    }
397
398    pub fn needs_reclip(&mut self, entity: Entity) {
399        self.style.needs_reclip(entity);
400        let iter = LayoutTreeIterator::subtree(&self.tree, entity);
401        for descendant in iter {
402            self.style.needs_reclip(descendant);
403        }
404    }
405
406    /// Mark the application as needing to rerun layout computations
407    pub fn needs_relayout(&mut self) {
408        self.style.needs_relayout(Entity::root());
409    }
410
411    pub(crate) fn set_system_flags(&mut self, entity: Entity, system_flags: SystemFlags) {
412        if system_flags.contains(SystemFlags::RELAYOUT) {
413            self.style.needs_relayout(entity);
414        }
415
416        if system_flags.contains(SystemFlags::RESTYLE) {
417            self.needs_restyle(entity);
418        }
419
420        if system_flags.contains(SystemFlags::REDRAW) {
421            self.needs_redraw(entity);
422        }
423
424        if system_flags.contains(SystemFlags::REFLOW) {
425            self.style.needs_text_update(entity);
426        }
427
428        if system_flags.contains(SystemFlags::RETRANSFORM) {
429            self.needs_retransform(entity);
430        }
431
432        if system_flags.contains(SystemFlags::RECLIP) {
433            self.needs_reclip(entity);
434        }
435
436        if system_flags.contains(SystemFlags::REACCESS) {
437            self.style.needs_access_update(entity);
438        }
439    }
440
441    /// Enables or disables PseudoClasses for the focus of an entity
442    pub(crate) fn set_focus_pseudo_classes(
443        &mut self,
444        focused: Entity,
445        enabled: bool,
446        focus_visible: bool,
447    ) {
448        if enabled {
449            debug!(
450                "Focus changed to {:?} parent: {:?}, view: {}, posx: {}, posy: {} width: {} height: {}",
451                focused,
452                self.tree.get_parent(focused),
453                self.views
454                    .get(&focused)
455                    .map_or("<None>", |view| view.element().unwrap_or("<Unnamed>")),
456                self.cache.get_posx(focused),
457                self.cache.get_posy(focused),
458                self.cache.get_width(focused),
459                self.cache.get_height(focused),
460            );
461        }
462
463        if let Some(pseudo_classes) = self.style.pseudo_classes.get_mut(focused) {
464            pseudo_classes.set(PseudoClassFlags::FOCUS, enabled);
465            if !enabled || focus_visible {
466                pseudo_classes.set(PseudoClassFlags::FOCUS_VISIBLE, enabled);
467                self.style.needs_access_update(focused);
468                self.needs_restyle(focused);
469            }
470        }
471
472        let ancestors = focused.parent_iter(&self.tree).collect::<Vec<_>>();
473        for entity in ancestors {
474            if let Some(pseudo_classes) = self.style.pseudo_classes.get_mut(entity) {
475                pseudo_classes.set(PseudoClassFlags::FOCUS_WITHIN, enabled);
476            }
477            self.needs_restyle(entity);
478        }
479    }
480
481    /// Sets application focus to the current entity with the specified focus visiblity
482    pub fn focus_with_visibility(&mut self, focus_visible: bool) {
483        let focusable = self.current == Entity::root()
484            || self
485                .style
486                .abilities
487                .get(self.current)
488                .is_some_and(|abilities| abilities.contains(Abilities::FOCUSABLE));
489        if !focusable {
490            return;
491        }
492
493        let old_focus = self.focused;
494        let new_focus = self.current;
495        self.set_focus_pseudo_classes(old_focus, false, focus_visible);
496        if self.current != self.focused {
497            self.emit_to(old_focus, WindowEvent::FocusOut);
498            self.emit_to(new_focus, WindowEvent::FocusIn);
499            self.focused = self.current;
500        }
501        self.set_focus_pseudo_classes(new_focus, true, focus_visible);
502
503        self.emit_custom(Event::new(WindowEvent::FocusVisibility(focus_visible)).target(old_focus));
504        self.emit_custom(Event::new(WindowEvent::FocusVisibility(focus_visible)).target(new_focus));
505
506        self.needs_restyle(self.focused);
507        self.needs_restyle(self.current);
508        self.style.needs_access_update(self.focused);
509        self.style.needs_access_update(self.current);
510    }
511
512    /// Sets application focus to the current entity using the previous focus visibility
513    pub fn focus(&mut self) {
514        let focused = self.focused;
515        let old_focus_visible = self
516            .style
517            .pseudo_classes
518            .get_mut(focused)
519            .filter(|class| class.contains(PseudoClassFlags::FOCUS_VISIBLE))
520            .is_some();
521        self.focus_with_visibility(old_focus_visible)
522    }
523
524    /// Removes the children of the provided entity from the application.
525    pub(crate) fn remove_children(&mut self, entity: Entity) {
526        let child_iter = ChildIterator::new(&self.tree, entity);
527        let children = child_iter.collect::<Vec<_>>();
528        for child in children.into_iter() {
529            self.remove(child);
530        }
531    }
532
533    /// Removes the provided entity from the application.
534    pub fn remove(&mut self, entity: Entity) {
535        let delete_list = entity.branch_iter(&self.tree).collect::<Vec<_>>();
536
537        if !delete_list.is_empty() {
538            self.style.needs_restyle(self.current);
539            // An absolutely-positioned node is out of its parent's flow, so removing it cannot
540            // change the parent's layout — no relayout is needed (the vacated region is unioned
541            // into the window's dirty_rect below). Otherwise relayout incrementally from the parent
542            // of the removed entity so its remaining children reflow, rather than the whole tree.
543            let is_absolute = self.style.position_type.get(entity).copied().unwrap_or_default()
544                == PositionType::Absolute;
545            if !is_absolute {
546                if let Some(parent) = self.tree.get_layout_parent(entity) {
547                    self.style.needs_relayout(parent);
548                } else {
549                    self.style.needs_relayout(entity);
550                }
551            }
552            self.needs_redraw(self.current);
553        }
554
555        for entity in delete_list.iter().rev() {
556            if let Some(mut view) = self.views.remove(entity) {
557                view.event(
558                    &mut EventContext::new_with_current(self, *entity),
559                    &mut Event::new(WindowEvent::Destroyed).direct(*entity),
560                );
561
562                self.views.insert(*entity, view);
563            }
564
565            if let Some(binding) = self.bindings.remove(entity) {
566                binding.remove(self);
567
568                self.bindings.insert(*entity, binding);
569            }
570
571            for image in self.resource_manager.images.values_mut() {
572                // no need to drop them here. garbage collection happens after draw (policy based)
573                image.observers.remove(entity);
574            }
575
576            if let Some(identifier) = self.style.ids.get(*entity) {
577                self.entity_identifiers.remove(identifier);
578            }
579
580            if let Some(index) = self.focus_stack.iter().position(|r| r == entity) {
581                self.focus_stack.remove(index);
582            }
583
584            if self.focused == *entity {
585                if let Some(new_focus) = self.focus_stack.pop() {
586                    self.with_current(new_focus, |cx| cx.focus());
587                } else {
588                    self.with_current(Entity::root(), |cx| cx.focus());
589                }
590            }
591
592            if self.captured == *entity {
593                self.captured = Entity::null();
594            }
595
596            if let Some(parent) = self.tree.get_layout_parent(*entity) {
597                self.style.needs_access_update(parent);
598            }
599
600            let mut stopped_timers = Vec::new();
601
602            for timer in self.running_timers.iter() {
603                if timer.entity == *entity {
604                    stopped_timers.push(timer.id);
605                }
606            }
607
608            for timer in stopped_timers {
609                self.stop_timer(timer);
610            }
611
612            let window_entity = self.tree.get_parent_window(*entity).unwrap_or(Entity::root());
613
614            if !self.tree.is_window(*entity) {
615                if let Some(draw_bounds) = self.cache.draw_bounds.get(*entity) {
616                    if let Some(dirty_rect) =
617                        &mut self.windows.get_mut(&window_entity).unwrap().dirty_rect
618                    {
619                        *dirty_rect = dirty_rect.union(draw_bounds);
620                    } else {
621                        self.windows.get_mut(&window_entity).unwrap().dirty_rect =
622                            Some(*draw_bounds);
623                    }
624                }
625            }
626
627            self.windows.get_mut(&window_entity).unwrap().redraw_list.remove(entity);
628
629            if self.windows.contains_key(entity) {
630                self.windows.remove(entity);
631                #[cfg(feature = "clipboard")]
632                self.clipboards.remove(entity);
633            }
634
635            self.tree.remove(*entity).expect("");
636            self.cache.remove(*entity);
637            self.style.remove(*entity);
638            self.models.remove(entity);
639            self.views.remove(entity);
640            self.text_context.text_bounds.remove(*entity);
641            self.text_context.text_paragraphs.remove(*entity);
642            self.entity_manager.destroy(*entity);
643        }
644    }
645
646    /// Sets whether a view should have the given class name.
647    pub fn toggle_class(&mut self, name: &str, applied: impl Res<bool>) {
648        let name = name.to_owned();
649        let entity = self.current();
650        let current = self.current();
651        self.with_current(current, |cx| {
652            applied.set_or_bind(cx, move |cx, applied| {
653                let applied = applied.get_value(cx);
654                if let Some(class_list) = cx.style.classes.get_mut(entity) {
655                    if applied {
656                        class_list.insert(name.clone());
657                    } else {
658                        class_list.remove(&name);
659                    }
660                }
661
662                cx.needs_restyle(entity);
663            });
664        });
665    }
666
667    /// Add a listener to an entity.
668    ///
669    /// A listener can be used to handle events which would not normally propagate to the entity.
670    /// For example, mouse events when a different entity has captured them. Useful for things like
671    /// closing a popup when clicking outside of its bounding box.
672    pub fn add_listener<F, W>(&mut self, listener: F)
673    where
674        W: View,
675        F: 'static + Fn(&mut W, &mut EventContext, &mut Event),
676    {
677        self.listeners.insert(
678            self.current,
679            Box::new(move |event_handler, context, event| {
680                if let Some(widget) = event_handler.downcast_mut::<W>() {
681                    (listener)(widget, context, event);
682                }
683            }),
684        );
685    }
686
687    /// Adds a global listener to the application.
688    ///
689    /// Global listeners have the first opportunity to handle every event that is sent in an
690    /// application. They will *never* be removed. If you need a listener tied to the lifetime of a
691    /// view, use `add_listener`.
692    pub fn add_global_listener<F>(&mut self, listener: F)
693    where
694        F: 'static + Fn(&mut EventContext, &mut Event),
695    {
696        self.global_listeners.push(Box::new(listener));
697    }
698
699    /// Adds a font to the application from memory.
700    pub fn add_font_mem(&mut self, data: impl AsRef<[u8]>) {
701        self.text_context.asset_provider.register_typeface(
702            self.text_context.default_font_manager.new_from_data(data.as_ref(), None).unwrap(),
703            None,
704        );
705    }
706
707    /// Returns the element name (e.g. `"textbox"`) of the currently focused
708    /// view, or `None` if the focused entity has no view or the view doesn't
709    /// declare an element name.
710    ///
711    /// Mirrors [`BackendContext::focused_element`] for use from contexts
712    /// (such as the `on_idle` application callback) that receive a
713    /// `&mut Context` directly.
714    pub fn focused_element(&self) -> Option<&'static str> {
715        self.views.get(&self.focused).and_then(|view| view.element())
716    }
717
718    pub fn add_stylesheet(&mut self, style: impl IntoCssStr) -> Result<(), std::io::Error> {
719        self.resource_manager.styles.push(Box::new(style));
720
721        EventContext::new(self).reload_styles().expect("Failed to reload styles");
722
723        Ok(())
724    }
725
726    /// Remove all user themes from the application.
727    pub fn add_built_in_styles(&mut self) {
728        self.add_built_in_translations();
729
730        let user_styles = if self.built_in_styles_added {
731            if self.resource_manager.styles.len() >= 3 {
732                self.resource_manager.styles.drain(3..).collect::<Vec<_>>()
733            } else {
734                Vec::new()
735            }
736        } else {
737            self.resource_manager.styles.drain(..).collect::<Vec<_>>()
738        };
739
740        self.resource_manager.styles.clear();
741
742        self.resource_manager.styles.push(Box::new(DEFAULT_LAYOUT));
743        self.resource_manager.styles.push(Box::new(MARKDOWN));
744
745        if !self.ignore_default_theme {
746            self.resource_manager.styles.push(Box::new(DEFAULT_THEME));
747            let environment = self.data::<Environment>();
748            let theme_mode = environment.effective_theme();
749            let direction = environment.direction.get();
750            self.with_current(Entity::root(), |cx| {
751                let cx = &mut EventContext::new(cx);
752                cx.toggle_class("dark", theme_mode == ThemeMode::DarkMode);
753                cx.toggle_class("rtl", direction == Direction::RightToLeft);
754            })
755        } else {
756            // Add an empty stylesheet to ensure that the list of styles contains at least three entries.
757            self.resource_manager.styles.push(Box::new(""));
758        }
759
760        self.resource_manager.styles.extend(user_styles);
761        self.built_in_styles_added = true;
762
763        EventContext::new(self).reload_styles().unwrap();
764    }
765
766    /// Adds built-in translations for default view strings.
767    pub fn add_built_in_translations(&mut self) {
768        if self.built_in_translations_added {
769            return;
770        }
771
772        self.add_translation("en-US".parse().unwrap(), DEFAULT_TRANSLATION_EN_US)
773            .expect("Failed to load built-in en-US translation resources");
774        self.built_in_translations_added = true;
775    }
776
777    pub fn add_animation(&mut self, animation: AnimationBuilder) -> Animation {
778        self.style.add_animation(animation)
779    }
780
781    pub fn set_image_loader<F: 'static + Fn(&mut ResourceContext, &str)>(&mut self, loader: F) {
782        self.resource_manager.image_loader = Some(Box::new(loader));
783    }
784
785    /// Adds a translation to the application for the provided language.
786    ///
787    /// Returns an error if the FTL syntax is invalid or the resource cannot be added to the bundle.
788    pub fn add_translation(
789        &mut self,
790        lang: LanguageIdentifier,
791        ftl: impl ToString,
792    ) -> Result<(), crate::resource::TranslationError> {
793        self.resource_manager.add_translation(lang, ftl.to_string())
794    }
795
796    /// Adds a timer to the application.
797    ///
798    /// `interval` - The time between ticks of the timer.
799    /// `duration` - An optional duration for the timer. Pass `None` for a continuos timer.
800    /// `callback` - A callback which is called on when the timer is started, ticks, and stops. Disambiguated by the `TimerAction` parameter of the callback.
801    ///
802    /// Returns a `Timer` id which can be used to start and stop the timer.  
803    ///
804    /// # Example
805    /// Creates a timer which calls the provided callback every second for 5 seconds:
806    /// ```rust
807    /// # use vizia_core::prelude::*;
808    /// # use instant::{Instant, Duration};
809    /// # let cx = &mut Context::default();
810    /// let timer = cx.add_timer(Duration::from_secs(1), Some(Duration::from_secs(5)), |cx, reason|{
811    ///     match reason {
812    ///         TimerAction::Start => {
813    ///             debug!("Start timer");
814    ///         }
815    ///     
816    ///         TimerAction::Tick(delta) => {
817    ///             debug!("Tick timer: {:?}", delta);
818    ///         }
819    ///
820    ///         TimerAction::Stop => {
821    ///             debug!("Stop timer");
822    ///         }
823    ///     }
824    /// });
825    /// ```
826    pub fn add_timer(
827        &mut self,
828        interval: Duration,
829        duration: Option<Duration>,
830        callback: impl Fn(&mut EventContext, TimerAction) + 'static,
831    ) -> Timer {
832        let id = Timer(self.timers.len());
833        self.timers.push(TimerState {
834            entity: Entity::root(),
835            id,
836            time: Instant::now(),
837            interval,
838            duration,
839            start_time: Instant::now(),
840            callback: Rc::new(callback),
841            ticking: false,
842            stopping: false,
843        });
844
845        id
846    }
847
848    /// Starts a timer with the provided timer id.
849    ///
850    /// Events sent within the timer callback provided in `add_timer()` will target the current view.
851    pub fn start_timer(&mut self, timer: Timer) {
852        let current = self.current;
853        if !self.timer_is_running(timer) {
854            let timer_state = self.timers[timer.0].clone();
855            // Copy timer state from pending to playing
856            self.running_timers.push(timer_state);
857        }
858
859        self.modify_timer(timer, |timer_state| {
860            let now = Instant::now();
861            timer_state.start_time = now;
862            timer_state.time = now;
863            timer_state.entity = current;
864            timer_state.ticking = false;
865            timer_state.stopping = false;
866        });
867    }
868
869    /// Modifies the state of an existing timer with the provided `Timer` id.
870    pub fn modify_timer(&mut self, timer: Timer, timer_function: impl Fn(&mut TimerState)) {
871        while let Some(next_timer_state) = self.running_timers.peek() {
872            if next_timer_state.id == timer {
873                let mut timer_state = self.running_timers.pop().unwrap();
874
875                (timer_function)(&mut timer_state);
876
877                self.running_timers.push(timer_state);
878
879                return;
880            }
881        }
882
883        for pending_timer in self.timers.iter_mut() {
884            if pending_timer.id == timer {
885                (timer_function)(pending_timer);
886            }
887        }
888    }
889
890    /// Returns true if the timer with the provided timer id is currently running.
891    pub fn timer_is_running(&mut self, timer: Timer) -> bool {
892        for timer_state in self.running_timers.iter() {
893            if timer_state.id == timer {
894                return true;
895            }
896        }
897
898        false
899    }
900
901    /// Stops the timer with the given timer id.
902    ///
903    /// Any events emitted in response to the timer stopping, as determined by the callback provided in `add_timer()`, will target the view which called `start_timer()`.
904    pub fn stop_timer(&mut self, timer: Timer) {
905        let mut running_timers = self.running_timers.clone();
906
907        for timer_state in running_timers.iter() {
908            if timer_state.id == timer {
909                (timer_state.callback)(
910                    &mut EventContext::new_with_current(self, timer_state.entity),
911                    TimerAction::Stop,
912                );
913            }
914        }
915
916        self.running_timers =
917            running_timers.drain().filter(|timer_state| timer_state.id != timer).collect();
918    }
919
920    // Tick all timers.
921    pub(crate) fn tick_timers(&mut self) {
922        let now = Instant::now();
923        while let Some(next_timer_state) = self.running_timers.peek() {
924            if next_timer_state.time <= now {
925                let mut timer_state = self.running_timers.pop().unwrap();
926
927                if timer_state.end_time().unwrap_or_else(|| now + Duration::from_secs(1)) >= now {
928                    if !timer_state.ticking {
929                        (timer_state.callback)(
930                            &mut EventContext::new_with_current(self, timer_state.entity),
931                            TimerAction::Start,
932                        );
933                        timer_state.ticking = true;
934                    } else {
935                        (timer_state.callback)(
936                            &mut EventContext::new_with_current(self, timer_state.entity),
937                            TimerAction::Tick(now - timer_state.time),
938                        );
939                    }
940                    timer_state.time = now + timer_state.interval - (now - timer_state.time);
941                    self.running_timers.push(timer_state);
942                } else {
943                    (timer_state.callback)(
944                        &mut EventContext::new_with_current(self, timer_state.entity),
945                        TimerAction::Stop,
946                    );
947                }
948            } else {
949                break;
950            }
951        }
952    }
953
954    /// Loads an image from memory and associates it with the provided path.
955    pub fn load_image(&mut self, path: &str, data: &'static [u8], policy: ImageRetentionPolicy) {
956        let id = if let Some(image_id) = self.resource_manager.image_ids.get(path) {
957            *image_id
958        } else {
959            let id = self.resource_manager.image_id_manager.create();
960            self.resource_manager.image_ids.insert(path.to_owned(), id);
961            id
962        };
963
964        if let Some(image) =
965            skia_safe::Image::from_encoded(unsafe { skia_safe::Data::new_bytes(data) })
966        {
967            match self.resource_manager.images.entry(id) {
968                Entry::Occupied(mut occ) => {
969                    occ.get_mut().image = ImageOrSvg::Image(image);
970                    occ.get_mut().dirty = true;
971                    occ.get_mut().retention_policy = policy;
972                }
973                Entry::Vacant(vac) => {
974                    vac.insert(StoredImage {
975                        image: ImageOrSvg::Image(image),
976                        retention_policy: policy,
977                        used: true,
978                        dirty: false,
979                        observers: HashSet::new(),
980                    });
981                }
982            }
983            self.style.needs_relayout(self.current);
984        }
985    }
986
987    pub fn load_svg(&mut self, path: &str, data: &[u8], policy: ImageRetentionPolicy) -> ImageId {
988        let id = if let Some(image_id) = self.resource_manager.image_ids.get(path) {
989            return *image_id;
990        } else {
991            let id = self.resource_manager.image_id_manager.create();
992            self.resource_manager.image_ids.insert(path.to_owned(), id);
993            id
994        };
995
996        if let Ok(svg) = svg::Dom::from_bytes(data, self.text_context.default_font_manager.clone())
997        {
998            match self.resource_manager.images.entry(id) {
999                Entry::Occupied(mut occ) => {
1000                    occ.get_mut().image = ImageOrSvg::Svg(svg);
1001                    occ.get_mut().dirty = true;
1002                    occ.get_mut().retention_policy = policy;
1003                }
1004                Entry::Vacant(vac) => {
1005                    vac.insert(StoredImage {
1006                        image: ImageOrSvg::Svg(svg),
1007                        retention_policy: policy,
1008                        used: true,
1009                        dirty: false,
1010                        observers: HashSet::new(),
1011                    });
1012                }
1013            }
1014            // Relayout only the entities that display this resource (its observers) plus the
1015            // current entity (e.g. the freshly-built Svg view triggering the load), rather than
1016            // forcing a full tree relayout.
1017            let observers: Vec<Entity> = self
1018                .resource_manager
1019                .images
1020                .get(&id)
1021                .map(|img| img.observers.iter().copied().collect())
1022                .unwrap_or_default();
1023            for observer in observers {
1024                self.style.needs_relayout(observer);
1025            }
1026            self.style.needs_relayout(self.current);
1027        }
1028
1029        id
1030    }
1031
1032    pub fn spawn<F>(&self, target: F)
1033    where
1034        F: 'static + Send + FnOnce(&mut ContextProxy),
1035    {
1036        let mut cxp = ContextProxy {
1037            current: self.current,
1038            event_proxy: self.event_proxy.as_ref().map(|p| p.make_clone()),
1039        };
1040
1041        std::thread::spawn(move || target(&mut cxp));
1042    }
1043
1044    pub fn get_proxy(&self) -> ContextProxy {
1045        ContextProxy {
1046            current: self.current,
1047            event_proxy: self.event_proxy.as_ref().map(|p| p.make_clone()),
1048        }
1049    }
1050
1051    #[cfg(feature = "tokio")]
1052    /// Submits a configured [`TaskBuilder`] for asynchronous execution.
1053    ///
1054    /// Tasks run on Vizia's shared Tokio runtime and complete through the
1055    /// `on_result(...)` callback attached to the builder, when one is provided.
1056    ///
1057    /// Returns a [`TaskHandle`] that can be used to request cancellation.
1058    ///
1059    /// # Example
1060    /// ```rust,no_run
1061    /// # use vizia_core::prelude::*;
1062    /// # #[cfg(feature = "tokio")]
1063    /// # {
1064    /// # let cx = Context::default();
1065    /// // Fire-and-forget:
1066    /// cx.add_task(Task::new(|_| async move { Ok::<(), &'static str>(()) }));
1067    ///
1068    /// // With completion handling:
1069    /// cx.add_task(
1070    ///     Task::new(|_| async move { Ok::<_, &'static str>("loaded") })
1071    ///         .on_result(|result, proxy| {
1072    ///             if let TaskResult::Completed(message) = result {
1073    ///                 let _ = proxy.emit(message);
1074    ///             }
1075    ///         }),
1076    /// );
1077    /// # }
1078    /// ```
1079    pub fn add_task<T, E>(&self, task: TaskBuilder<T, E>) -> TaskHandle
1080    where
1081        T: Send + 'static,
1082        E: Send + 'static,
1083    {
1084        task.add_to_context(self)
1085    }
1086
1087    /// Finds the entity that identifier identifies
1088    pub(crate) fn resolve_entity_identifier(&self, identity: &str) -> Option<Entity> {
1089        self.entity_identifiers.get(identity).cloned()
1090    }
1091
1092    pub fn set_ime_state(&mut self, new_state: ImeState) {
1093        self.ime_state = new_state;
1094    }
1095}
1096
1097pub(crate) enum InternalEvent {
1098    Redraw,
1099    LoadImage { path: String, image: Mutex<Option<skia_safe::Image>>, policy: ImageRetentionPolicy },
1100}
1101
1102pub struct LocalizationContext<'a> {
1103    pub(crate) current: Entity,
1104    pub(crate) resource_manager: &'a ResourceManager,
1105    pub(crate) models: &'a Models,
1106    pub(crate) views: &'a Views,
1107    pub(crate) tree: &'a Tree<Entity>,
1108}
1109
1110impl<'a> LocalizationContext<'a> {
1111    pub(crate) fn from_context(cx: &'a Context) -> Self {
1112        Self {
1113            current: cx.current,
1114            resource_manager: &cx.resource_manager,
1115            models: &cx.models,
1116            views: &cx.views,
1117            tree: &cx.tree,
1118        }
1119    }
1120
1121    pub(crate) fn from_event_context(cx: &'a EventContext) -> Self {
1122        Self {
1123            current: cx.current,
1124            resource_manager: cx.resource_manager,
1125            models: cx.models,
1126            views: cx.views,
1127            tree: cx.tree,
1128        }
1129    }
1130
1131    pub(crate) fn environment(&self) -> &Environment {
1132        self.data::<Environment>()
1133    }
1134}
1135
1136/// A trait for any Context-like object that lets you access stored model data.
1137///
1138/// This lets resource reads be generic over any of these types.
1139pub trait DataContext {
1140    /// Get model/view data from the context. Returns `None` if the data does not exist.
1141    fn try_data<T: 'static>(&self) -> Option<&T>;
1142
1143    /// Get model/view data from the context. Panics if the data does not exist.
1144    fn data<T: 'static>(&self) -> &T {
1145        self.try_data::<T>().expect("data not found in context")
1146    }
1147
1148    /// Convert the current context into a [LocalizationContext].
1149    fn localization_context(&self) -> Option<LocalizationContext<'_>> {
1150        None
1151    }
1152}
1153
1154/// A trait for any Context-like object that lets you emit events.
1155pub trait EmitContext {
1156    /// Send an event containing the provided message up the tree from the current entity.
1157    ///
1158    /// # Example
1159    /// ```rust
1160    /// # use vizia_core::prelude::*;
1161    /// # use instant::{Instant, Duration};
1162    /// # let cx = &mut Context::default();
1163    /// # enum AppEvent {Increment}
1164    /// cx.emit(AppEvent::Increment);
1165    /// ```
1166    fn emit<M: Any>(&mut self, message: M);
1167
1168    /// Send an event containing the provided message directly to a specified entity from the current entity.
1169    ///
1170    /// # Example
1171    /// ```rust
1172    /// # use vizia_core::prelude::*;
1173    /// # use instant::{Instant, Duration};
1174    /// # let cx = &mut Context::default();
1175    /// # enum AppEvent {Increment}
1176    /// cx.emit_to(Entity::root(), AppEvent::Increment);
1177    /// ```
1178    fn emit_to<M: Any>(&mut self, target: Entity, message: M);
1179
1180    /// Send a custom event with custom origin and propagation information.
1181    ///
1182    /// # Example
1183    /// ```rust
1184    /// # use vizia_core::prelude::*;
1185    /// # use instant::{Instant, Duration};
1186    /// # let cx = &mut Context::default();
1187    /// # enum AppEvent {Increment}
1188    /// cx.emit_custom(
1189    ///     Event::new(AppEvent::Increment)
1190    ///         .origin(cx.current())
1191    ///         .target(Entity::root())
1192    ///         .propagate(Propagation::Subtree)
1193    /// );
1194    /// ```
1195    fn emit_custom(&mut self, event: Event);
1196
1197    /// Send an event containing the provided message up the tree at a particular time instant.
1198    ///
1199    /// Returns a `TimedEventHandle` which can be used to cancel the scheduled event.
1200    ///
1201    /// # Example
1202    /// Emit an event after a delay of 2 seconds:
1203    /// ```rust
1204    /// # use vizia_core::prelude::*;
1205    /// # use instant::{Instant, Duration};
1206    /// # let cx = &mut Context::default();
1207    /// # enum AppEvent {Increment}
1208    /// cx.schedule_emit(AppEvent::Increment, Instant::now() + Duration::from_secs(2));
1209    /// ```
1210    fn schedule_emit<M: Any>(&mut self, message: M, at: Instant) -> TimedEventHandle;
1211
1212    /// Send an event containing the provided message directly to a specified view at a particular time instant.
1213    ///
1214    /// Returns a `TimedEventHandle` which can be used to cancel the scheduled event.
1215    ///
1216    /// # Example
1217    /// Emit an event to the root view (window) after a delay of 2 seconds:
1218    /// ```rust
1219    /// # use vizia_core::prelude::*;
1220    /// # use instant::{Instant, Duration};
1221    /// # let cx = &mut Context::default();
1222    /// # enum AppEvent {Increment}
1223    /// cx.schedule_emit_to(Entity::root(), AppEvent::Increment, Instant::now() + Duration::from_secs(2));
1224    /// ```
1225    fn schedule_emit_to<M: Any>(
1226        &mut self,
1227        target: Entity,
1228        message: M,
1229        at: Instant,
1230    ) -> TimedEventHandle;
1231
1232    /// Send a custom event with custom origin and propagation information at a particular time instant.
1233    ///
1234    /// Returns a `TimedEventHandle` which can be used to cancel the scheduled event.
1235    ///
1236    /// # Example
1237    /// Emit a custom event after a delay of 2 seconds:
1238    /// ```rust
1239    /// # use vizia_core::prelude::*;
1240    /// # use instant::{Instant, Duration};
1241    /// # let cx = &mut Context::default();
1242    /// # enum AppEvent {Increment}
1243    /// cx.schedule_emit_custom(    
1244    ///     Event::new(AppEvent::Increment)
1245    ///         .target(Entity::root())
1246    ///         .origin(cx.current())
1247    ///         .propagate(Propagation::Subtree),
1248    ///     Instant::now() + Duration::from_secs(2)
1249    /// );
1250    /// ```
1251    fn schedule_emit_custom(&mut self, event: Event, at: Instant) -> TimedEventHandle;
1252
1253    /// Cancel a scheduled event before it is sent.
1254    ///
1255    /// # Example
1256    /// ```rust
1257    /// # use vizia_core::prelude::*;
1258    /// # use instant::{Instant, Duration};
1259    /// # let cx = &mut Context::default();
1260    /// # enum AppEvent {Increment}
1261    /// let timed_event = cx.schedule_emit_to(Entity::root(), AppEvent::Increment, Instant::now() + Duration::from_secs(2));
1262    /// cx.cancel_scheduled(timed_event);
1263    /// ```
1264    fn cancel_scheduled(&mut self, handle: TimedEventHandle);
1265}
1266
1267impl DataContext for Context {
1268    fn try_data<T: 'static>(&self) -> Option<&T> {
1269        // return data for the static model.
1270        if let Some(t) = <dyn Any>::downcast_ref::<T>(&()) {
1271            return Some(t);
1272        }
1273
1274        for entity in self.current.parent_iter(&self.tree) {
1275            // Return any model data.
1276            if let Some(models) = self.models.get(&entity) {
1277                if let Some(model) = models.get(&TypeId::of::<T>()) {
1278                    return model.downcast_ref::<T>();
1279                }
1280            }
1281
1282            // Return any view data.
1283            if let Some(view_handler) = self.views.get(&entity) {
1284                if let Some(data) = view_handler.downcast_ref::<T>() {
1285                    return Some(data);
1286                }
1287            }
1288        }
1289
1290        None
1291    }
1292
1293    fn localization_context(&self) -> Option<LocalizationContext<'_>> {
1294        Some(LocalizationContext::from_context(self))
1295    }
1296}
1297
1298impl DataContext for LocalizationContext<'_> {
1299    fn try_data<T: 'static>(&self) -> Option<&T> {
1300        // return data for the static model.
1301        if let Some(t) = <dyn Any>::downcast_ref::<T>(&()) {
1302            return Some(t);
1303        }
1304
1305        for entity in self.current.parent_iter(self.tree) {
1306            // Return any model data.
1307            if let Some(models) = self.models.get(&entity) {
1308                if let Some(model) = models.get(&TypeId::of::<T>()) {
1309                    return model.downcast_ref::<T>();
1310                }
1311            }
1312
1313            // Return any view data.
1314            if let Some(view_handler) = self.views.get(&entity) {
1315                if let Some(data) = view_handler.downcast_ref::<T>() {
1316                    return Some(data);
1317                }
1318            }
1319        }
1320
1321        None
1322    }
1323}
1324
1325impl EmitContext for Context {
1326    fn emit<M: Any>(&mut self, message: M) {
1327        self.event_queue.push_back(
1328            Event::new(message)
1329                .target(self.current)
1330                .origin(self.current)
1331                .propagate(Propagation::Up),
1332        );
1333    }
1334
1335    fn emit_to<M: Any>(&mut self, target: Entity, message: M) {
1336        self.event_queue.push_back(
1337            Event::new(message).target(target).origin(self.current).propagate(Propagation::Direct),
1338        );
1339    }
1340
1341    fn emit_custom(&mut self, event: Event) {
1342        self.event_queue.push_back(event);
1343    }
1344
1345    fn schedule_emit<M: Any>(&mut self, message: M, at: Instant) -> TimedEventHandle {
1346        self.schedule_emit_custom(
1347            Event::new(message)
1348                .target(self.current)
1349                .origin(self.current)
1350                .propagate(Propagation::Up),
1351            at,
1352        )
1353    }
1354
1355    fn schedule_emit_to<M: Any>(
1356        &mut self,
1357        target: Entity,
1358        message: M,
1359        at: Instant,
1360    ) -> TimedEventHandle {
1361        self.schedule_emit_custom(
1362            Event::new(message).target(target).origin(self.current).propagate(Propagation::Direct),
1363            at,
1364        )
1365    }
1366
1367    fn schedule_emit_custom(&mut self, event: Event, at: Instant) -> TimedEventHandle {
1368        let handle = TimedEventHandle(self.next_event_id);
1369        self.event_schedule.push(TimedEvent { event, time: at, ident: handle });
1370        self.next_event_id += 1;
1371        handle
1372    }
1373
1374    fn cancel_scheduled(&mut self, handle: TimedEventHandle) {
1375        self.event_schedule =
1376            self.event_schedule.drain().filter(|item| item.ident != handle).collect();
1377    }
1378}