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) 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 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 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();
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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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
1110pub trait DataContext {
1114 fn try_data<T: 'static>(&self) -> Option<&T>;
1116
1117 fn data<T: 'static>(&self) -> &T {
1119 self.try_data::<T>().expect("data not found in context")
1120 }
1121
1122 fn localization_context(&self) -> Option<LocalizationContext<'_>> {
1124 None
1125 }
1126}
1127
1128pub trait EmitContext {
1130 fn emit<M: Any + Send>(&mut self, message: M);
1141
1142 fn emit_to<M: Any + Send>(&mut self, target: Entity, message: M);
1153
1154 fn emit_custom(&mut self, event: Event);
1170
1171 fn schedule_emit<M: Any + Send>(&mut self, message: M, at: Instant) -> TimedEventHandle;
1185
1186 fn schedule_emit_to<M: Any + Send>(
1200 &mut self,
1201 target: Entity,
1202 message: M,
1203 at: Instant,
1204 ) -> TimedEventHandle;
1205
1206 fn schedule_emit_custom(&mut self, event: Event, at: Instant) -> TimedEventHandle;
1226
1227 fn cancel_scheduled(&mut self, handle: TimedEventHandle);
1239}
1240
1241impl DataContext for Context {
1242 fn try_data<T: 'static>(&self) -> Option<&T> {
1243 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 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 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 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 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 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}