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) clipboard: 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            clipboard: default_clipboard_provider(),
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();
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();
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.needs_relayout();
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            self.style.needs_relayout();
540            self.needs_redraw(self.current);
541        }
542
543        for entity in delete_list.iter().rev() {
544            if let Some(mut view) = self.views.remove(entity) {
545                view.event(
546                    &mut EventContext::new_with_current(self, *entity),
547                    &mut Event::new(WindowEvent::Destroyed).direct(*entity),
548                );
549
550                self.views.insert(*entity, view);
551            }
552
553            if let Some(binding) = self.bindings.remove(entity) {
554                binding.remove(self);
555
556                self.bindings.insert(*entity, binding);
557            }
558
559            for image in self.resource_manager.images.values_mut() {
560                // no need to drop them here. garbage collection happens after draw (policy based)
561                image.observers.remove(entity);
562            }
563
564            if let Some(identifier) = self.style.ids.get(*entity) {
565                self.entity_identifiers.remove(identifier);
566            }
567
568            if let Some(index) = self.focus_stack.iter().position(|r| r == entity) {
569                self.focus_stack.remove(index);
570            }
571
572            if self.focused == *entity {
573                if let Some(new_focus) = self.focus_stack.pop() {
574                    self.with_current(new_focus, |cx| cx.focus());
575                } else {
576                    self.with_current(Entity::root(), |cx| cx.focus());
577                }
578            }
579
580            if self.captured == *entity {
581                self.captured = Entity::null();
582            }
583
584            if let Some(parent) = self.tree.get_layout_parent(*entity) {
585                self.style.needs_access_update(parent);
586            }
587
588            let mut stopped_timers = Vec::new();
589
590            for timer in self.running_timers.iter() {
591                if timer.entity == *entity {
592                    stopped_timers.push(timer.id);
593                }
594            }
595
596            for timer in stopped_timers {
597                self.stop_timer(timer);
598            }
599
600            let window_entity = self.tree.get_parent_window(*entity).unwrap_or(Entity::root());
601
602            if !self.tree.is_window(*entity) {
603                if let Some(draw_bounds) = self.cache.draw_bounds.get(*entity) {
604                    if let Some(dirty_rect) =
605                        &mut self.windows.get_mut(&window_entity).unwrap().dirty_rect
606                    {
607                        *dirty_rect = dirty_rect.union(draw_bounds);
608                    } else {
609                        self.windows.get_mut(&window_entity).unwrap().dirty_rect =
610                            Some(*draw_bounds);
611                    }
612                }
613            }
614
615            self.windows.get_mut(&window_entity).unwrap().redraw_list.remove(entity);
616
617            if self.windows.contains_key(entity) {
618                self.windows.remove(entity);
619            }
620
621            self.tree.remove(*entity).expect("");
622            self.cache.remove(*entity);
623            self.style.remove(*entity);
624            self.models.remove(entity);
625            self.views.remove(entity);
626            self.text_context.text_bounds.remove(*entity);
627            self.text_context.text_paragraphs.remove(*entity);
628            self.entity_manager.destroy(*entity);
629        }
630    }
631
632    /// Sets whether a view should have the given class name.
633    pub fn toggle_class(&mut self, name: &str, applied: impl Res<bool>) {
634        let name = name.to_owned();
635        let entity = self.current();
636        let current = self.current();
637        self.with_current(current, |cx| {
638            applied.set_or_bind(cx, move |cx, applied| {
639                let applied = applied.get_value(cx);
640                if let Some(class_list) = cx.style.classes.get_mut(entity) {
641                    if applied {
642                        class_list.insert(name.clone());
643                    } else {
644                        class_list.remove(&name);
645                    }
646                }
647
648                cx.needs_restyle(entity);
649            });
650        });
651    }
652
653    /// Add a listener to an entity.
654    ///
655    /// A listener can be used to handle events which would not normally propagate to the entity.
656    /// For example, mouse events when a different entity has captured them. Useful for things like
657    /// closing a popup when clicking outside of its bounding box.
658    pub fn add_listener<F, W>(&mut self, listener: F)
659    where
660        W: View,
661        F: 'static + Fn(&mut W, &mut EventContext, &mut Event),
662    {
663        self.listeners.insert(
664            self.current,
665            Box::new(move |event_handler, context, event| {
666                if let Some(widget) = event_handler.downcast_mut::<W>() {
667                    (listener)(widget, context, event);
668                }
669            }),
670        );
671    }
672
673    /// Adds a global listener to the application.
674    ///
675    /// Global listeners have the first opportunity to handle every event that is sent in an
676    /// application. They will *never* be removed. If you need a listener tied to the lifetime of a
677    /// view, use `add_listener`.
678    pub fn add_global_listener<F>(&mut self, listener: F)
679    where
680        F: 'static + Fn(&mut EventContext, &mut Event),
681    {
682        self.global_listeners.push(Box::new(listener));
683    }
684
685    /// Adds a font to the application from memory.
686    pub fn add_font_mem(&mut self, data: impl AsRef<[u8]>) {
687        self.text_context.asset_provider.register_typeface(
688            self.text_context.default_font_manager.new_from_data(data.as_ref(), None).unwrap(),
689            None,
690        );
691    }
692
693    /// Returns the element name (e.g. `"textbox"`) of the currently focused
694    /// view, or `None` if the focused entity has no view or the view doesn't
695    /// declare an element name.
696    ///
697    /// Mirrors [`BackendContext::focused_element`] for use from contexts
698    /// (such as the `on_idle` application callback) that receive a
699    /// `&mut Context` directly.
700    pub fn focused_element(&self) -> Option<&'static str> {
701        self.views.get(&self.focused).and_then(|view| view.element())
702    }
703
704    pub fn add_stylesheet(&mut self, style: impl IntoCssStr) -> Result<(), std::io::Error> {
705        self.resource_manager.styles.push(Box::new(style));
706
707        EventContext::new(self).reload_styles().expect("Failed to reload styles");
708
709        Ok(())
710    }
711
712    /// Remove all user themes from the application.
713    pub fn add_built_in_styles(&mut self) {
714        self.add_built_in_translations();
715
716        let user_styles = if self.built_in_styles_added {
717            if self.resource_manager.styles.len() >= 3 {
718                self.resource_manager.styles.drain(3..).collect::<Vec<_>>()
719            } else {
720                Vec::new()
721            }
722        } else {
723            self.resource_manager.styles.drain(..).collect::<Vec<_>>()
724        };
725
726        self.resource_manager.styles.clear();
727
728        self.resource_manager.styles.push(Box::new(DEFAULT_LAYOUT));
729        self.resource_manager.styles.push(Box::new(MARKDOWN));
730
731        if !self.ignore_default_theme {
732            self.resource_manager.styles.push(Box::new(DEFAULT_THEME));
733            let environment = self.data::<Environment>();
734            let theme_mode = environment.effective_theme();
735            let direction = environment.direction.get();
736            self.with_current(Entity::root(), |cx| {
737                let cx = &mut EventContext::new(cx);
738                cx.toggle_class("dark", theme_mode == ThemeMode::DarkMode);
739                cx.toggle_class("rtl", direction == Direction::RightToLeft);
740            })
741        } else {
742            // Add an empty stylesheet to ensure that the list of styles contains at least three entries.
743            self.resource_manager.styles.push(Box::new(""));
744        }
745
746        self.resource_manager.styles.extend(user_styles);
747        self.built_in_styles_added = true;
748
749        EventContext::new(self).reload_styles().unwrap();
750    }
751
752    /// Adds built-in translations for default view strings.
753    pub fn add_built_in_translations(&mut self) {
754        if self.built_in_translations_added {
755            return;
756        }
757
758        self.add_translation("en-US".parse().unwrap(), DEFAULT_TRANSLATION_EN_US)
759            .expect("Failed to load built-in en-US translation resources");
760        self.built_in_translations_added = true;
761    }
762
763    pub fn add_animation(&mut self, animation: AnimationBuilder) -> Animation {
764        self.style.add_animation(animation)
765    }
766
767    pub fn set_image_loader<F: 'static + Fn(&mut ResourceContext, &str)>(&mut self, loader: F) {
768        self.resource_manager.image_loader = Some(Box::new(loader));
769    }
770
771    /// Adds a translation to the application for the provided language.
772    ///
773    /// Returns an error if the FTL syntax is invalid or the resource cannot be added to the bundle.
774    pub fn add_translation(
775        &mut self,
776        lang: LanguageIdentifier,
777        ftl: impl ToString,
778    ) -> Result<(), crate::resource::TranslationError> {
779        self.resource_manager.add_translation(lang, ftl.to_string())
780    }
781
782    /// Adds a timer to the application.
783    ///
784    /// `interval` - The time between ticks of the timer.
785    /// `duration` - An optional duration for the timer. Pass `None` for a continuos timer.
786    /// `callback` - A callback which is called on when the timer is started, ticks, and stops. Disambiguated by the `TimerAction` parameter of the callback.
787    ///
788    /// Returns a `Timer` id which can be used to start and stop the timer.  
789    ///
790    /// # Example
791    /// Creates a timer which calls the provided callback every second for 5 seconds:
792    /// ```rust
793    /// # use vizia_core::prelude::*;
794    /// # use instant::{Instant, Duration};
795    /// # let cx = &mut Context::default();
796    /// let timer = cx.add_timer(Duration::from_secs(1), Some(Duration::from_secs(5)), |cx, reason|{
797    ///     match reason {
798    ///         TimerAction::Start => {
799    ///             debug!("Start timer");
800    ///         }
801    ///     
802    ///         TimerAction::Tick(delta) => {
803    ///             debug!("Tick timer: {:?}", delta);
804    ///         }
805    ///
806    ///         TimerAction::Stop => {
807    ///             debug!("Stop timer");
808    ///         }
809    ///     }
810    /// });
811    /// ```
812    pub fn add_timer(
813        &mut self,
814        interval: Duration,
815        duration: Option<Duration>,
816        callback: impl Fn(&mut EventContext, TimerAction) + 'static,
817    ) -> Timer {
818        let id = Timer(self.timers.len());
819        self.timers.push(TimerState {
820            entity: Entity::root(),
821            id,
822            time: Instant::now(),
823            interval,
824            duration,
825            start_time: Instant::now(),
826            callback: Rc::new(callback),
827            ticking: false,
828            stopping: false,
829        });
830
831        id
832    }
833
834    /// Starts a timer with the provided timer id.
835    ///
836    /// Events sent within the timer callback provided in `add_timer()` will target the current view.
837    pub fn start_timer(&mut self, timer: Timer) {
838        let current = self.current;
839        if !self.timer_is_running(timer) {
840            let timer_state = self.timers[timer.0].clone();
841            // Copy timer state from pending to playing
842            self.running_timers.push(timer_state);
843        }
844
845        self.modify_timer(timer, |timer_state| {
846            let now = Instant::now();
847            timer_state.start_time = now;
848            timer_state.time = now;
849            timer_state.entity = current;
850            timer_state.ticking = false;
851            timer_state.stopping = false;
852        });
853    }
854
855    /// Modifies the state of an existing timer with the provided `Timer` id.
856    pub fn modify_timer(&mut self, timer: Timer, timer_function: impl Fn(&mut TimerState)) {
857        while let Some(next_timer_state) = self.running_timers.peek() {
858            if next_timer_state.id == timer {
859                let mut timer_state = self.running_timers.pop().unwrap();
860
861                (timer_function)(&mut timer_state);
862
863                self.running_timers.push(timer_state);
864
865                return;
866            }
867        }
868
869        for pending_timer in self.timers.iter_mut() {
870            if pending_timer.id == timer {
871                (timer_function)(pending_timer);
872            }
873        }
874    }
875
876    /// Returns true if the timer with the provided timer id is currently running.
877    pub fn timer_is_running(&mut self, timer: Timer) -> bool {
878        for timer_state in self.running_timers.iter() {
879            if timer_state.id == timer {
880                return true;
881            }
882        }
883
884        false
885    }
886
887    /// Stops the timer with the given timer id.
888    ///
889    /// 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()`.
890    pub fn stop_timer(&mut self, timer: Timer) {
891        let mut running_timers = self.running_timers.clone();
892
893        for timer_state in running_timers.iter() {
894            if timer_state.id == timer {
895                (timer_state.callback)(
896                    &mut EventContext::new_with_current(self, timer_state.entity),
897                    TimerAction::Stop,
898                );
899            }
900        }
901
902        self.running_timers =
903            running_timers.drain().filter(|timer_state| timer_state.id != timer).collect();
904    }
905
906    // Tick all timers.
907    pub(crate) fn tick_timers(&mut self) {
908        let now = Instant::now();
909        while let Some(next_timer_state) = self.running_timers.peek() {
910            if next_timer_state.time <= now {
911                let mut timer_state = self.running_timers.pop().unwrap();
912
913                if timer_state.end_time().unwrap_or_else(|| now + Duration::from_secs(1)) >= now {
914                    if !timer_state.ticking {
915                        (timer_state.callback)(
916                            &mut EventContext::new_with_current(self, timer_state.entity),
917                            TimerAction::Start,
918                        );
919                        timer_state.ticking = true;
920                    } else {
921                        (timer_state.callback)(
922                            &mut EventContext::new_with_current(self, timer_state.entity),
923                            TimerAction::Tick(now - timer_state.time),
924                        );
925                    }
926                    timer_state.time = now + timer_state.interval - (now - timer_state.time);
927                    self.running_timers.push(timer_state);
928                } else {
929                    (timer_state.callback)(
930                        &mut EventContext::new_with_current(self, timer_state.entity),
931                        TimerAction::Stop,
932                    );
933                }
934            } else {
935                break;
936            }
937        }
938    }
939
940    /// Loads an image from memory and associates it with the provided path.
941    pub fn load_image(&mut self, path: &str, data: &'static [u8], policy: ImageRetentionPolicy) {
942        let id = if let Some(image_id) = self.resource_manager.image_ids.get(path) {
943            *image_id
944        } else {
945            let id = self.resource_manager.image_id_manager.create();
946            self.resource_manager.image_ids.insert(path.to_owned(), id);
947            id
948        };
949
950        if let Some(image) =
951            skia_safe::Image::from_encoded(unsafe { skia_safe::Data::new_bytes(data) })
952        {
953            match self.resource_manager.images.entry(id) {
954                Entry::Occupied(mut occ) => {
955                    occ.get_mut().image = ImageOrSvg::Image(image);
956                    occ.get_mut().dirty = true;
957                    occ.get_mut().retention_policy = policy;
958                }
959                Entry::Vacant(vac) => {
960                    vac.insert(StoredImage {
961                        image: ImageOrSvg::Image(image),
962                        retention_policy: policy,
963                        used: true,
964                        dirty: false,
965                        observers: HashSet::new(),
966                    });
967                }
968            }
969            self.style.needs_relayout();
970        }
971    }
972
973    pub fn load_svg(&mut self, path: &str, data: &[u8], policy: ImageRetentionPolicy) -> ImageId {
974        let id = if let Some(image_id) = self.resource_manager.image_ids.get(path) {
975            return *image_id;
976        } else {
977            let id = self.resource_manager.image_id_manager.create();
978            self.resource_manager.image_ids.insert(path.to_owned(), id);
979            id
980        };
981
982        if let Ok(svg) = svg::Dom::from_bytes(data, self.text_context.default_font_manager.clone())
983        {
984            match self.resource_manager.images.entry(id) {
985                Entry::Occupied(mut occ) => {
986                    occ.get_mut().image = ImageOrSvg::Svg(svg);
987                    occ.get_mut().dirty = true;
988                    occ.get_mut().retention_policy = policy;
989                }
990                Entry::Vacant(vac) => {
991                    vac.insert(StoredImage {
992                        image: ImageOrSvg::Svg(svg),
993                        retention_policy: policy,
994                        used: true,
995                        dirty: false,
996                        observers: HashSet::new(),
997                    });
998                }
999            }
1000            self.style.needs_relayout();
1001        }
1002
1003        id
1004    }
1005
1006    pub fn spawn<F>(&self, target: F)
1007    where
1008        F: 'static + Send + FnOnce(&mut ContextProxy),
1009    {
1010        let mut cxp = ContextProxy {
1011            current: self.current,
1012            event_proxy: self.event_proxy.as_ref().map(|p| p.make_clone()),
1013        };
1014
1015        std::thread::spawn(move || target(&mut cxp));
1016    }
1017
1018    pub fn get_proxy(&self) -> ContextProxy {
1019        ContextProxy {
1020            current: self.current,
1021            event_proxy: self.event_proxy.as_ref().map(|p| p.make_clone()),
1022        }
1023    }
1024
1025    #[cfg(feature = "tokio")]
1026    /// Submits a configured [`TaskBuilder`] for asynchronous execution.
1027    ///
1028    /// Tasks run on Vizia's shared Tokio runtime and complete through the
1029    /// `on_result(...)` callback attached to the builder, when one is provided.
1030    ///
1031    /// Returns a [`TaskHandle`] that can be used to request cancellation.
1032    ///
1033    /// # Example
1034    /// ```rust,no_run
1035    /// # use vizia_core::prelude::*;
1036    /// # #[cfg(feature = "tokio")]
1037    /// # {
1038    /// # let cx = Context::default();
1039    /// // Fire-and-forget:
1040    /// cx.add_task(Task::new(|_| async move { Ok::<(), &'static str>(()) }));
1041    ///
1042    /// // With completion handling:
1043    /// cx.add_task(
1044    ///     Task::new(|_| async move { Ok::<_, &'static str>("loaded") })
1045    ///         .on_result(|result, proxy| {
1046    ///             if let TaskResult::Completed(message) = result {
1047    ///                 let _ = proxy.emit(message);
1048    ///             }
1049    ///         }),
1050    /// );
1051    /// # }
1052    /// ```
1053    pub fn add_task<T, E>(&self, task: TaskBuilder<T, E>) -> TaskHandle
1054    where
1055        T: Send + 'static,
1056        E: Send + 'static,
1057    {
1058        task.add_to_context(self)
1059    }
1060
1061    /// Finds the entity that identifier identifies
1062    pub(crate) fn resolve_entity_identifier(&self, identity: &str) -> Option<Entity> {
1063        self.entity_identifiers.get(identity).cloned()
1064    }
1065
1066    pub fn set_ime_state(&mut self, new_state: ImeState) {
1067        self.ime_state = new_state;
1068    }
1069}
1070
1071pub(crate) enum InternalEvent {
1072    Redraw,
1073    LoadImage { path: String, image: Mutex<Option<skia_safe::Image>>, policy: ImageRetentionPolicy },
1074}
1075
1076pub struct LocalizationContext<'a> {
1077    pub(crate) current: Entity,
1078    pub(crate) resource_manager: &'a ResourceManager,
1079    pub(crate) models: &'a Models,
1080    pub(crate) views: &'a Views,
1081    pub(crate) tree: &'a Tree<Entity>,
1082}
1083
1084impl<'a> LocalizationContext<'a> {
1085    pub(crate) fn from_context(cx: &'a Context) -> Self {
1086        Self {
1087            current: cx.current,
1088            resource_manager: &cx.resource_manager,
1089            models: &cx.models,
1090            views: &cx.views,
1091            tree: &cx.tree,
1092        }
1093    }
1094
1095    pub(crate) fn from_event_context(cx: &'a EventContext) -> Self {
1096        Self {
1097            current: cx.current,
1098            resource_manager: cx.resource_manager,
1099            models: cx.models,
1100            views: cx.views,
1101            tree: cx.tree,
1102        }
1103    }
1104
1105    pub(crate) fn environment(&self) -> &Environment {
1106        self.data::<Environment>()
1107    }
1108}
1109
1110/// A trait for any Context-like object that lets you access stored model data.
1111///
1112/// This lets resource reads be generic over any of these types.
1113pub trait DataContext {
1114    /// Get model/view data from the context. Returns `None` if the data does not exist.
1115    fn try_data<T: 'static>(&self) -> Option<&T>;
1116
1117    /// Get model/view data from the context. Panics if the data does not exist.
1118    fn data<T: 'static>(&self) -> &T {
1119        self.try_data::<T>().expect("data not found in context")
1120    }
1121
1122    /// Convert the current context into a [LocalizationContext].
1123    fn localization_context(&self) -> Option<LocalizationContext<'_>> {
1124        None
1125    }
1126}
1127
1128/// A trait for any Context-like object that lets you emit events.
1129pub trait EmitContext {
1130    /// Send an event containing the provided message up the tree from the current entity.
1131    ///
1132    /// # Example
1133    /// ```rust
1134    /// # use vizia_core::prelude::*;
1135    /// # use instant::{Instant, Duration};
1136    /// # let cx = &mut Context::default();
1137    /// # enum AppEvent {Increment}
1138    /// cx.emit(AppEvent::Increment);
1139    /// ```
1140    fn emit<M: Any + Send>(&mut self, message: M);
1141
1142    /// Send an event containing the provided message directly to a specified entity from the current entity.
1143    ///
1144    /// # Example
1145    /// ```rust
1146    /// # use vizia_core::prelude::*;
1147    /// # use instant::{Instant, Duration};
1148    /// # let cx = &mut Context::default();
1149    /// # enum AppEvent {Increment}
1150    /// cx.emit_to(Entity::root(), AppEvent::Increment);
1151    /// ```
1152    fn emit_to<M: Any + Send>(&mut self, target: Entity, message: M);
1153
1154    /// Send a custom event with custom origin and propagation information.
1155    ///
1156    /// # Example
1157    /// ```rust
1158    /// # use vizia_core::prelude::*;
1159    /// # use instant::{Instant, Duration};
1160    /// # let cx = &mut Context::default();
1161    /// # enum AppEvent {Increment}
1162    /// cx.emit_custom(
1163    ///     Event::new(AppEvent::Increment)
1164    ///         .origin(cx.current())
1165    ///         .target(Entity::root())
1166    ///         .propagate(Propagation::Subtree)
1167    /// );
1168    /// ```
1169    fn emit_custom(&mut self, event: Event);
1170
1171    /// Send an event containing the provided message up the tree at a particular time instant.
1172    ///
1173    /// Returns a `TimedEventHandle` which can be used to cancel the scheduled event.
1174    ///
1175    /// # Example
1176    /// Emit an event after a delay of 2 seconds:
1177    /// ```rust
1178    /// # use vizia_core::prelude::*;
1179    /// # use instant::{Instant, Duration};
1180    /// # let cx = &mut Context::default();
1181    /// # enum AppEvent {Increment}
1182    /// cx.schedule_emit(AppEvent::Increment, Instant::now() + Duration::from_secs(2));
1183    /// ```
1184    fn schedule_emit<M: Any + Send>(&mut self, message: M, at: Instant) -> TimedEventHandle;
1185
1186    /// Send an event containing the provided message directly to a specified view at a particular time instant.
1187    ///
1188    /// Returns a `TimedEventHandle` which can be used to cancel the scheduled event.
1189    ///
1190    /// # Example
1191    /// Emit an event to the root view (window) after a delay of 2 seconds:
1192    /// ```rust
1193    /// # use vizia_core::prelude::*;
1194    /// # use instant::{Instant, Duration};
1195    /// # let cx = &mut Context::default();
1196    /// # enum AppEvent {Increment}
1197    /// cx.schedule_emit_to(Entity::root(), AppEvent::Increment, Instant::now() + Duration::from_secs(2));
1198    /// ```
1199    fn schedule_emit_to<M: Any + Send>(
1200        &mut self,
1201        target: Entity,
1202        message: M,
1203        at: Instant,
1204    ) -> TimedEventHandle;
1205
1206    /// Send a custom event with custom origin and propagation information at a particular time instant.
1207    ///
1208    /// Returns a `TimedEventHandle` which can be used to cancel the scheduled event.
1209    ///
1210    /// # Example
1211    /// Emit a custom event after a delay of 2 seconds:
1212    /// ```rust
1213    /// # use vizia_core::prelude::*;
1214    /// # use instant::{Instant, Duration};
1215    /// # let cx = &mut Context::default();
1216    /// # enum AppEvent {Increment}
1217    /// cx.schedule_emit_custom(    
1218    ///     Event::new(AppEvent::Increment)
1219    ///         .target(Entity::root())
1220    ///         .origin(cx.current())
1221    ///         .propagate(Propagation::Subtree),
1222    ///     Instant::now() + Duration::from_secs(2)
1223    /// );
1224    /// ```
1225    fn schedule_emit_custom(&mut self, event: Event, at: Instant) -> TimedEventHandle;
1226
1227    /// Cancel a scheduled event before it is sent.
1228    ///
1229    /// # Example
1230    /// ```rust
1231    /// # use vizia_core::prelude::*;
1232    /// # use instant::{Instant, Duration};
1233    /// # let cx = &mut Context::default();
1234    /// # enum AppEvent {Increment}
1235    /// let timed_event = cx.schedule_emit_to(Entity::root(), AppEvent::Increment, Instant::now() + Duration::from_secs(2));
1236    /// cx.cancel_scheduled(timed_event);
1237    /// ```
1238    fn cancel_scheduled(&mut self, handle: TimedEventHandle);
1239}
1240
1241impl DataContext for Context {
1242    fn try_data<T: 'static>(&self) -> Option<&T> {
1243        // return data for the static model.
1244        if let Some(t) = <dyn Any>::downcast_ref::<T>(&()) {
1245            return Some(t);
1246        }
1247
1248        for entity in self.current.parent_iter(&self.tree) {
1249            // Return any model data.
1250            if let Some(models) = self.models.get(&entity) {
1251                if let Some(model) = models.get(&TypeId::of::<T>()) {
1252                    return model.downcast_ref::<T>();
1253                }
1254            }
1255
1256            // Return any view data.
1257            if let Some(view_handler) = self.views.get(&entity) {
1258                if let Some(data) = view_handler.downcast_ref::<T>() {
1259                    return Some(data);
1260                }
1261            }
1262        }
1263
1264        None
1265    }
1266
1267    fn localization_context(&self) -> Option<LocalizationContext<'_>> {
1268        Some(LocalizationContext::from_context(self))
1269    }
1270}
1271
1272impl DataContext for LocalizationContext<'_> {
1273    fn try_data<T: 'static>(&self) -> Option<&T> {
1274        // return data for the static model.
1275        if let Some(t) = <dyn Any>::downcast_ref::<T>(&()) {
1276            return Some(t);
1277        }
1278
1279        for entity in self.current.parent_iter(self.tree) {
1280            // Return any model data.
1281            if let Some(models) = self.models.get(&entity) {
1282                if let Some(model) = models.get(&TypeId::of::<T>()) {
1283                    return model.downcast_ref::<T>();
1284                }
1285            }
1286
1287            // Return any view data.
1288            if let Some(view_handler) = self.views.get(&entity) {
1289                if let Some(data) = view_handler.downcast_ref::<T>() {
1290                    return Some(data);
1291                }
1292            }
1293        }
1294
1295        None
1296    }
1297}
1298
1299impl EmitContext for Context {
1300    fn emit<M: Any + Send>(&mut self, message: M) {
1301        self.event_queue.push_back(
1302            Event::new(message)
1303                .target(self.current)
1304                .origin(self.current)
1305                .propagate(Propagation::Up),
1306        );
1307    }
1308
1309    fn emit_to<M: Any + Send>(&mut self, target: Entity, message: M) {
1310        self.event_queue.push_back(
1311            Event::new(message).target(target).origin(self.current).propagate(Propagation::Direct),
1312        );
1313    }
1314
1315    fn emit_custom(&mut self, event: Event) {
1316        self.event_queue.push_back(event);
1317    }
1318
1319    fn schedule_emit<M: Any + Send>(&mut self, message: M, at: Instant) -> TimedEventHandle {
1320        self.schedule_emit_custom(
1321            Event::new(message)
1322                .target(self.current)
1323                .origin(self.current)
1324                .propagate(Propagation::Up),
1325            at,
1326        )
1327    }
1328
1329    fn schedule_emit_to<M: Any + Send>(
1330        &mut self,
1331        target: Entity,
1332        message: M,
1333        at: Instant,
1334    ) -> TimedEventHandle {
1335        self.schedule_emit_custom(
1336            Event::new(message).target(target).origin(self.current).propagate(Propagation::Direct),
1337            at,
1338        )
1339    }
1340
1341    fn schedule_emit_custom(&mut self, event: Event, at: Instant) -> TimedEventHandle {
1342        let handle = TimedEventHandle(self.next_event_id);
1343        self.event_schedule.push(TimedEvent { event, time: at, ident: handle });
1344        self.next_event_id += 1;
1345        handle
1346    }
1347
1348    fn cancel_scheduled(&mut self, handle: TimedEventHandle) {
1349        self.event_schedule =
1350            self.event_schedule.drain().filter(|item| item.ident != handle).collect();
1351    }
1352}