1mod 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 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
146pub 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 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 result.style.dpi_factor = 1.0;
305
306 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 pub fn current(&self) -> Entity {
329 self.current
330 }
331
332 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 pub fn environment(&self) -> &Environment {
343 self.data::<Environment>()
344 }
345
346 pub fn parent_window(&self) -> Entity {
348 self.tree.get_parent_window(self.current).unwrap_or(Entity::root())
349 }
350
351 pub fn scale_factor(&self) -> f32 {
353 self.style.dpi_factor as f32
354 }
355
356 pub fn needs_redraw(&mut self, entity: Entity) {
358 if self.entity_manager.is_alive(entity) {
359 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 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 }
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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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
1136pub trait DataContext {
1140 fn try_data<T: 'static>(&self) -> Option<&T>;
1142
1143 fn data<T: 'static>(&self) -> &T {
1145 self.try_data::<T>().expect("data not found in context")
1146 }
1147
1148 fn localization_context(&self) -> Option<LocalizationContext<'_>> {
1150 None
1151 }
1152}
1153
1154pub trait EmitContext {
1156 fn emit<M: Any>(&mut self, message: M);
1167
1168 fn emit_to<M: Any>(&mut self, target: Entity, message: M);
1179
1180 fn emit_custom(&mut self, event: Event);
1196
1197 fn schedule_emit<M: Any>(&mut self, message: M, at: Instant) -> TimedEventHandle;
1211
1212 fn schedule_emit_to<M: Any>(
1226 &mut self,
1227 target: Entity,
1228 message: M,
1229 at: Instant,
1230 ) -> TimedEventHandle;
1231
1232 fn schedule_emit_custom(&mut self, event: Event, at: Instant) -> TimedEventHandle;
1252
1253 fn cancel_scheduled(&mut self, handle: TimedEventHandle);
1265}
1266
1267impl DataContext for Context {
1268 fn try_data<T: 'static>(&self) -> Option<&T> {
1269 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 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 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 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 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 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}