signal.rs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. use crate::{read::Readable, ReadableRef};
  2. use crate::{write::Writable, GlobalKey};
  3. use crate::{WritableRef, Write};
  4. use dioxus_core::{prelude::ScopeId, Runtime};
  5. use generational_box::{BorrowResult, UnsyncStorage};
  6. use std::ops::Deref;
  7. use super::get_global_context;
  8. use crate::read_impls;
  9. use crate::Signal;
  10. /// A signal that can be accessed from anywhere in the application and created in a static
  11. pub struct GlobalSignal<T> {
  12. initializer: fn() -> T,
  13. key: GlobalKey<'static>,
  14. }
  15. impl<T: 'static> GlobalSignal<T> {
  16. /// Create a new global signal with the given initializer.
  17. #[track_caller]
  18. pub const fn new(initializer: fn() -> T) -> GlobalSignal<T> {
  19. let key = std::panic::Location::caller();
  20. GlobalSignal {
  21. initializer,
  22. key: GlobalKey::new(key),
  23. }
  24. }
  25. /// Get the key for this global
  26. pub fn key(&self) -> GlobalKey<'static> {
  27. self.key.clone()
  28. }
  29. /// Create this global signal with a specific key.
  30. /// This is useful for ensuring that the signal is unique across the application and accessible from
  31. /// outside the application too.
  32. pub const fn with_key(initializer: fn() -> T, key: &'static str) -> GlobalSignal<T> {
  33. GlobalSignal {
  34. initializer,
  35. key: GlobalKey::new_from_str(key),
  36. }
  37. }
  38. /// Get the signal that backs this .
  39. pub fn signal(&self) -> Signal<T> {
  40. let key = self.key();
  41. let context = get_global_context();
  42. let read = context.signal.borrow();
  43. match read.get(&key) {
  44. Some(signal) => *signal.downcast_ref::<Signal<T>>().unwrap(),
  45. None => {
  46. drop(read);
  47. // Constructors are always run in the root scope
  48. // The signal also exists in the root scope
  49. let value = ScopeId::ROOT.in_runtime(self.initializer);
  50. let signal = Signal::new_in_scope(value, ScopeId::ROOT);
  51. let entry = context.signal.borrow_mut().insert(key, Box::new(signal));
  52. debug_assert!(entry.is_none(), "Global signal already exists");
  53. signal
  54. }
  55. }
  56. }
  57. #[doc(hidden)]
  58. pub fn maybe_with_rt<O>(&self, f: impl FnOnce(&T) -> O) -> O {
  59. if Runtime::current().is_err() {
  60. f(&(self.initializer)())
  61. } else {
  62. self.with(f)
  63. }
  64. }
  65. /// Write this value
  66. pub fn write(&self) -> Write<'static, T, UnsyncStorage> {
  67. self.signal().try_write_unchecked().unwrap()
  68. }
  69. /// Get the scope the signal was created in.
  70. pub fn origin_scope(&self) -> ScopeId {
  71. ScopeId::ROOT
  72. }
  73. /// Run a closure with a mutable reference to the signal's value.
  74. /// If the signal has been dropped, this will panic.
  75. #[track_caller]
  76. pub fn with_mut<O>(&self, f: impl FnOnce(&mut T) -> O) -> O {
  77. self.signal().with_mut(f)
  78. }
  79. /// Get the generational id of the signal.
  80. pub fn id(&self) -> generational_box::GenerationalBoxId {
  81. self.signal().id()
  82. }
  83. }
  84. impl<T: 'static> Readable for GlobalSignal<T> {
  85. type Target = T;
  86. type Storage = UnsyncStorage;
  87. #[track_caller]
  88. fn try_read_unchecked(
  89. &self,
  90. ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
  91. self.signal().try_read_unchecked()
  92. }
  93. #[track_caller]
  94. fn try_peek_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>> {
  95. self.signal().try_peek_unchecked()
  96. }
  97. }
  98. impl<T: 'static> Writable for GlobalSignal<T> {
  99. type Mut<'a, R: ?Sized + 'static> = Write<'a, R, UnsyncStorage>;
  100. fn map_mut<I: ?Sized, U: ?Sized + 'static, F: FnOnce(&mut I) -> &mut U>(
  101. ref_: Self::Mut<'_, I>,
  102. f: F,
  103. ) -> Self::Mut<'_, U> {
  104. Write::map(ref_, f)
  105. }
  106. fn try_map_mut<
  107. I: ?Sized + 'static,
  108. U: ?Sized + 'static,
  109. F: FnOnce(&mut I) -> Option<&mut U>,
  110. >(
  111. ref_: Self::Mut<'_, I>,
  112. f: F,
  113. ) -> Option<Self::Mut<'_, U>> {
  114. Write::filter_map(ref_, f)
  115. }
  116. fn downcast_lifetime_mut<'a: 'b, 'b, R: ?Sized + 'static>(
  117. mut_: Self::Mut<'a, R>,
  118. ) -> Self::Mut<'b, R> {
  119. Write::downcast_lifetime(mut_)
  120. }
  121. #[track_caller]
  122. fn try_write_unchecked(
  123. &self,
  124. ) -> Result<WritableRef<'static, Self>, generational_box::BorrowMutError> {
  125. self.signal().try_write_unchecked()
  126. }
  127. }
  128. /// Allow calling a signal with signal() syntax
  129. ///
  130. /// Currently only limited to copy types, though could probably specialize for string/arc/rc
  131. impl<T: Clone + 'static> Deref for GlobalSignal<T> {
  132. type Target = dyn Fn() -> T;
  133. fn deref(&self) -> &Self::Target {
  134. Readable::deref_impl(self)
  135. }
  136. }
  137. read_impls!(GlobalSignal<T>);