-
Notifications
You must be signed in to change notification settings - Fork 765
Implement .as_weak() and .upgrade() for global component instances #9436
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -974,6 +974,175 @@ mod weak_handle { | |||||||
|
|
||||||||
| pub use weak_handle::*; | ||||||||
|
|
||||||||
| /// This trait provides the necessary functionality for allowing creating strongly-referenced | ||||||||
| /// clones and conversion into a weak pointer for a Global slint component. | ||||||||
| /// | ||||||||
| /// This trait is implemented by the [generated component](index.html#generated-components) | ||||||||
| pub trait GlobalComponentHandle { | ||||||||
| /// The type for the public global component interface. | ||||||||
| #[doc(hidden)] | ||||||||
| type Global<'a>; | ||||||||
| /// The internal Inner type for `Weak<Self>::inner`. | ||||||||
| #[doc(hidden)] | ||||||||
| type WeakInner: Clone + Default; | ||||||||
| /// The internal Inner type for the 'Pin<sp::Rc<InnerSelf>'. | ||||||||
| #[doc(hidden)] | ||||||||
| type PinnedInner: Clone; | ||||||||
|
|
||||||||
| /// Internal function used when upgrading a weak reference to a strong one. | ||||||||
| #[doc(hidden)] | ||||||||
| fn upgrade_from_weak_inner(inner: &Self::WeakInner) -> Option<Self::PinnedInner> | ||||||||
| where | ||||||||
| Self: Sized; | ||||||||
|
|
||||||||
| /// Internal function used when upgrading a weak reference to a strong one. | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||||||||
| #[doc(hidden)] | ||||||||
| fn to_self<'a>(inner: &'a Self::PinnedInner) -> Self::Global<'a> | ||||||||
| where | ||||||||
| Self: Sized; | ||||||||
| } | ||||||||
|
|
||||||||
| pub use global_weak_handle::*; | ||||||||
|
|
||||||||
| mod global_weak_handle { | ||||||||
| use super::*; | ||||||||
|
|
||||||||
| /// Struct that's used to hold weak references of a [Slint global component](index.html#generated-components) | ||||||||
| /// | ||||||||
| /// In order to create a GlobalWeak, you should call .as_weak() on the global component instance. | ||||||||
| pub struct GlobalWeak<T: GlobalComponentHandle> { | ||||||||
| inner: T::WeakInner, | ||||||||
| #[cfg(feature = "std")] | ||||||||
| thread: std::thread::ThreadId, | ||||||||
| } | ||||||||
|
|
||||||||
| /// Struct that's used to hold a strong reference of a Slint global component | ||||||||
| pub struct GlobalStrong<T: GlobalComponentHandle>(T::PinnedInner); | ||||||||
|
|
||||||||
| impl<T: GlobalComponentHandle> GlobalStrong<T> { | ||||||||
| /// Get the actual global component | ||||||||
| pub fn global<'a>(&'a self) -> T::Global<'a> { | ||||||||
| T::to_self(&self.0) | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| impl<T: GlobalComponentHandle> Default for GlobalWeak<T> { | ||||||||
| fn default() -> Self { | ||||||||
| Self { | ||||||||
| inner: T::WeakInner::default(), | ||||||||
| #[cfg(feature = "std")] | ||||||||
| thread: std::thread::current().id(), | ||||||||
| } | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| impl<T: GlobalComponentHandle> Clone for GlobalWeak<T> { | ||||||||
| fn clone(&self) -> Self { | ||||||||
| Self { | ||||||||
| inner: self.inner.clone(), | ||||||||
| #[cfg(feature = "std")] | ||||||||
| thread: self.thread, | ||||||||
| } | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| impl<T: GlobalComponentHandle> GlobalWeak<T> { | ||||||||
| #[doc(hidden)] | ||||||||
| pub fn new(inner: T::WeakInner) -> Self { | ||||||||
| Self { | ||||||||
| inner, | ||||||||
| #[cfg(feature = "std")] | ||||||||
| thread: std::thread::current().id(), | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| /// Returns a new GlobalStrong struct, where it's possible to get the global component | ||||||||
| /// struct interface. If some other instance still holds a strong reference. | ||||||||
| /// Otherwise, returns None. | ||||||||
| /// | ||||||||
| /// This also returns None if the current thread is not the thread that created | ||||||||
| /// the component | ||||||||
| pub fn upgrade(&self) -> Option<GlobalStrong<T>> { | ||||||||
| #[cfg(feature = "std")] | ||||||||
| if std::thread::current().id() != self.thread { | ||||||||
| return None; | ||||||||
| } | ||||||||
| let inner = T::upgrade_from_weak_inner(&self.inner)?; | ||||||||
| Some(GlobalStrong(inner.clone())) | ||||||||
| } | ||||||||
|
|
||||||||
| /// Convenience function where a given functor is called with the global component | ||||||||
| /// | ||||||||
| /// This function must be called from the thread that created the component and | ||||||||
| /// component instance must exist, otherwise it will panic. | ||||||||
| pub fn upgrade_in(&self, func: impl FnOnce(T::Global<'_>)) { | ||||||||
| #[cfg(feature = "std")] | ||||||||
| if std::thread::current().id() != self.thread { | ||||||||
| panic!("Weak global component reference can't be accessed from a different thread"); | ||||||||
| } | ||||||||
|
|
||||||||
| match T::upgrade_from_weak_inner(&self.inner) { | ||||||||
| None => panic!("The global component instance doesn't exist anymore"), | ||||||||
| Some(inner) => func(T::to_self(&inner)), | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| /// Convenience function that combines [`invoke_from_event_loop()`] with [`Self::upgrade()`] | ||||||||
| /// | ||||||||
| /// The given functor will be added to an internal queue and will wake the event loop. | ||||||||
| /// On the next iteration of the event loop, the functor will be executed with a `T` as an argument. | ||||||||
| /// | ||||||||
| /// If the component was dropped because there are no more strong reference to the component, | ||||||||
| /// the functor will not be called. | ||||||||
| /// # Example | ||||||||
| /// ```rust | ||||||||
| /// # i_slint_backend_testing::init_no_event_loop(); | ||||||||
| /// slint::slint! { | ||||||||
| /// export global MyAppData { in property<int> foo; } | ||||||||
| /// export component MyApp inherits Window { /* ... */ } | ||||||||
| /// } | ||||||||
| /// let ui = MyApp::new().unwrap(); | ||||||||
| /// let my_app_data = ui.global::<MyAppData>(); | ||||||||
| /// let my_app_data_weak = my_app_data.as_weak(); | ||||||||
| /// | ||||||||
| /// let thread = std::thread::spawn(move || { | ||||||||
| /// // ... Do some computation in the thread | ||||||||
| /// let foo = 42; | ||||||||
| /// # assert!(my_app_data_weak.upgrade().is_none()); // note that upgrade fails in a thread | ||||||||
| /// # return; // don't upgrade_in_event_loop in our examples | ||||||||
| /// // now forward the data to the main thread using upgrade_in_event_loop | ||||||||
| /// my_app_data_weak.upgrade_in_event_loop(move |my_app_data| my_app_data.set_foo(foo)); | ||||||||
| /// }); | ||||||||
| /// # thread.join().unwrap(); return; // don't run the event loop in examples | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm confused. This is docu, not actual example, why the commented-out code? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a lot of copy / paste from the same function for Weak - at line 930 |
||||||||
| /// ui.run().unwrap(); | ||||||||
| /// ``` | ||||||||
| #[cfg(any(feature = "std", feature = "unsafe-single-threaded"))] | ||||||||
| pub fn upgrade_in_event_loop( | ||||||||
| &self, | ||||||||
| func: impl FnOnce(T::Global<'_>) + Send + 'static, | ||||||||
| ) -> Result<(), EventLoopError> | ||||||||
| where | ||||||||
| T: 'static, | ||||||||
| { | ||||||||
| let weak_handle = self.clone(); | ||||||||
| super::invoke_from_event_loop(move || { | ||||||||
| if let Some(h) = weak_handle.upgrade() { | ||||||||
| func(h.global()); | ||||||||
| } | ||||||||
| }) | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| // Safety: we make sure in upgrade that the thread is the proper one, | ||||||||
| // and the Weak only use atomic pointer so it is safe to clone and drop in another thread | ||||||||
| #[allow(unsafe_code)] | ||||||||
| #[cfg(any(feature = "std", feature = "unsafe-single-threaded"))] | ||||||||
| unsafe impl<T: GlobalComponentHandle> Send for GlobalWeak<T> {} | ||||||||
| #[allow(unsafe_code)] | ||||||||
| #[cfg(any(feature = "std", feature = "unsafe-single-threaded"))] | ||||||||
| unsafe impl<T: GlobalComponentHandle> Sync for GlobalWeak<T> {} | ||||||||
| } | ||||||||
|
|
||||||||
| /// Adds the specified function to an internal queue, notifies the event loop to wake up. | ||||||||
| /// Once woken up, any queued up functors will be invoked. | ||||||||
| /// | ||||||||
|
|
||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this needed as part of this feature?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to remember,... but it's due to the PinnedInner type, unfortunately to make this work, this may need to be 'pub'