Rename sixtyfps::IntoWeak into sixtyfps::ComponentHandle and remove Clone from generated components

`ComponentHandle` offers a richer API of common functions, such as
`show()`/`hide()` as well as the new `clone_strong()` - that should help
to prevent mistakely created strong reference that may cause leaks.

Fixes #188
This commit is contained in:
Simon Hausmann 2021-03-15 15:44:36 +01:00
parent 7236e0dee6
commit b27034efa5
4 changed files with 102 additions and 64 deletions

View file

@ -55,7 +55,7 @@ pub mod layouting {
pub mod generated_code { pub mod generated_code {
use crate::re_exports; use crate::re_exports;
use crate::IntoWeak; use crate::ComponentHandle;
use crate::Weak; use crate::Weak;
/// This an example of the API that is generated for a component in `.60` design markup. This may help you understand /// This an example of the API that is generated for a component in `.60` design markup. This may help you understand
@ -76,23 +76,7 @@ pub mod generated_code {
pub fn new() -> Self { pub fn new() -> Self {
unimplemented!() unimplemented!()
} }
/// Marks the window of this component to be shown on the screen. This registers
/// the window with the windowing system. In order to react to events from the windowing system,
/// such as draw requests or mouse/touch input, it is still necessary to spin the event loop,
/// using [`crate::run_event_loop`].
pub fn show(&self) {
unimplemented!()
}
/// Marks the window of this component to be hidden on the screen. This de-registers
/// the window from the windowing system and it will not receive any further events.
pub fn hide(&self) {
unimplemented!()
}
/// This is a convenience function that first calls [`Self::show`], followed by [`crate::run_event_loop()`]
/// and [`Self::hide`].
pub fn run(&self) {
unimplemented!()
}
/// A getter is generated for each property declared at the root of the component. /// A getter is generated for each property declared at the root of the component.
/// In this case, this is the getter that returns the value of the `counter` /// In this case, this is the getter that returns the value of the `counter`
/// property declared in the `.60` design markup. /// property declared in the `.60` design markup.
@ -117,7 +101,7 @@ pub mod generated_code {
/// is generated. This is the function that registers the function f as callback when the /// is generated. This is the function that registers the function f as callback when the
/// callback `hello` is emitted. In order to access /// callback `hello` is emitted. In order to access
/// the component in the callback, you'd typically capture a weak reference obtained using /// the component in the callback, you'd typically capture a weak reference obtained using
/// [`IntoWeak::as_weak`] /// [`ComponentHandle::as_weak`]
/// and then upgrade it to a strong reference when the callback is run: /// and then upgrade it to a strong reference when the callback is run:
/// ```ignore /// ```ignore
/// let sample = SampleComponent::new(); /// let sample = SampleComponent::new();
@ -130,15 +114,43 @@ pub mod generated_code {
pub fn on_hello(&self, f: impl Fn() + 'static) {} pub fn on_hello(&self, f: impl Fn() + 'static) {}
} }
impl IntoWeak for SampleComponent { impl ComponentHandle for SampleComponent {
#[doc(hidden)] #[doc(hidden)]
type Inner = SampleComponent; type Inner = SampleComponent;
/// Returns a new weak pointer.
fn as_weak(&self) -> Weak<Self> { fn as_weak(&self) -> Weak<Self> {
unimplemented!() unimplemented!()
} }
/// Returns a clone of this handle that's a strong reference.
fn clone_strong(&self) -> Self {
unimplemented!();
}
#[doc(hidden)] #[doc(hidden)]
fn from_inner(_: vtable::VRc<re_exports::ComponentVTable, Self::Inner>) -> Self { fn from_inner(_: vtable::VRc<re_exports::ComponentVTable, Self::Inner>) -> Self {
unimplemented!(); unimplemented!();
} }
/// Marks the window of this component to be shown on the screen. This registers
/// the window with the windowing system. In order to react to events from the windowing system,
/// such as draw requests or mouse/touch input, it is still necessary to spin the event loop,
/// using [`crate::run_event_loop`].
fn show(&self) {
unimplemented!();
}
/// Marks the window of this component to be hidden on the screen. This de-registers
/// the window from the windowing system and it will not receive any further events.
fn hide(&self) {
unimplemented!();
}
/// This is a convenience function that first calls [`Self::show`], followed by [`crate::run_event_loop()`]
/// and [`Self::hide`].
fn run(&self) {
unimplemented!();
}
} }
} }

View file

@ -96,9 +96,11 @@ it consist of a struct of the same name of the component.
For example, if you have `export MyComponent := Window { /*...*/ }` in the .60 file, it will create a `struct MyComponent{ /*...*/ }`. For example, if you have `export MyComponent := Window { /*...*/ }` in the .60 file, it will create a `struct MyComponent{ /*...*/ }`.
This documentation contains a documented generated component: [`docs::generated_code::SampleComponent`]. This documentation contains a documented generated component: [`docs::generated_code::SampleComponent`].
The following associated function are added to the component: The component is created using the [`fn new() -> Self`](docs::generated_code::SampleComponent::new) function. In addition
the following convenience functions are available through the [ComponentHandle] implementation:
- [`fn new() -> Self`](docs::generated_code::SampleComponent::new): to instantiate the component. - [`fn clone_strong(&self)`](docs::generated_code::SampleComponent::clone_strong): to create a strongly referenced clone.
- [`fn as_weak(&self)`](docs::generated_code::SampleComponent::as_weak): to create a weak reference.
- [`fn show(&self)`](docs::generated_code::SampleComponent::show): to show the window of the component. - [`fn show(&self)`](docs::generated_code::SampleComponent::show): to show the window of the component.
- [`fn hide(&self)`](docs::generated_code::SampleComponent::hide): to hide the window of the component. - [`fn hide(&self)`](docs::generated_code::SampleComponent::hide): to hide the window of the component.
- [`fn run(&self)`](docs::generated_code::SampleComponent::run): a convenience function that first calls `show()`, - [`fn run(&self)`](docs::generated_code::SampleComponent::run): a convenience function that first calls `show()`,
@ -242,9 +244,10 @@ pub fn run_event_loop() {
sixtyfps_rendering_backend_default::backend().run_event_loop(); sixtyfps_rendering_backend_default::backend().run_event_loop();
} }
/// This trait describes the conversion of a strongly referenced SixtyFPS component, /// This trait describes the common public API of a strongly referenced SixtyFPS component,
/// held by a [vtable::VRc] into a weak reference. /// held by a [vtable::VRc]. It allows creating strongly-referenced clones, a conversion into
pub trait IntoWeak { /// a weak pointer as well as other convenience functions.
pub trait ComponentHandle {
/// The type of the generated component. /// The type of the generated component.
#[doc(hidden)] #[doc(hidden)]
type Inner; type Inner;
@ -253,23 +256,40 @@ pub trait IntoWeak {
where where
Self: Sized; Self: Sized;
/// Returns a clone of this handle that's a strong reference.
fn clone_strong(&self) -> Self;
/// Internal function used when upgrading a weak reference to a strong one. /// Internal function used when upgrading a weak reference to a strong one.
#[doc(hidden)] #[doc(hidden)]
fn from_inner(_: vtable::VRc<re_exports::ComponentVTable, Self::Inner>) -> Self; fn from_inner(_: vtable::VRc<re_exports::ComponentVTable, Self::Inner>) -> Self;
/// Marks the window of this component to be shown on the screen. This registers
/// the window with the windowing system. In order to react to events from the windowing system,
/// such as draw requests or mouse/touch input, it is still necessary to spin the event loop,
/// using [`crate::run_event_loop`].
fn show(&self);
/// Marks the window of this component to be hidden on the screen. This de-registers
/// the window from the windowing system and it will not receive any further events.
fn hide(&self);
/// This is a convenience function that first calls [`Self::show`], followed by [`crate::run_event_loop()`]
/// and [`Self::hide`].
fn run(&self);
} }
/// Struct that's used to hold weak references for SixtyFPS components. /// Struct that's used to hold weak references for SixtyFPS components.
pub struct Weak<T: IntoWeak> { pub struct Weak<T: ComponentHandle> {
inner: vtable::VWeak<re_exports::ComponentVTable, T::Inner>, inner: vtable::VWeak<re_exports::ComponentVTable, T::Inner>,
} }
impl<T: IntoWeak> Clone for Weak<T> { impl<T: ComponentHandle> Clone for Weak<T> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { inner: self.inner.clone() } Self { inner: self.inner.clone() }
} }
} }
impl<T: IntoWeak> Weak<T> { impl<T: ComponentHandle> Weak<T> {
#[doc(hidden)] #[doc(hidden)]
pub fn new(rc: &vtable::VRc<re_exports::ComponentVTable, T::Inner>) -> Self { pub fn new(rc: &vtable::VRc<re_exports::ComponentVTable, T::Inner>) -> Self {
Self { inner: vtable::VRc::downgrade(&rc) } Self { inner: vtable::VRc::downgrade(&rc) }
@ -279,7 +299,7 @@ impl<T: IntoWeak> Weak<T> {
/// holds a strong reference. Otherwise, returns None. /// holds a strong reference. Otherwise, returns None.
pub fn upgrade(&self) -> Option<T> pub fn upgrade(&self) -> Option<T>
where where
T: IntoWeak, T: ComponentHandle,
{ {
self.inner.upgrade().map(|inner| T::from_inner(inner)) self.inner.upgrade().map(|inner| T::from_inner(inner))
} }
@ -297,6 +317,8 @@ pub mod testing {
use core::cell::Cell; use core::cell::Cell;
thread_local!(static KEYBOARD_MODIFIERS : Cell<crate::re_exports::KeyboardModifiers> = Default::default()); thread_local!(static KEYBOARD_MODIFIERS : Cell<crate::re_exports::KeyboardModifiers> = Default::default());
use super::ComponentHandle;
/// This trait gives access to the underyling Window of a component for the /// This trait gives access to the underyling Window of a component for the
/// purposes of testing. /// purposes of testing.
pub trait HasWindow { pub trait HasWindow {
@ -305,16 +327,17 @@ pub mod testing {
} }
pub use sixtyfps_corelib::tests::sixtyfps_mock_elapsed_time as mock_elapsed_time; pub use sixtyfps_corelib::tests::sixtyfps_mock_elapsed_time as mock_elapsed_time;
/// Simulate a mouse click /// Simulate a mouse click
pub fn send_mouse_click< pub fn send_mouse_click<
X: vtable::HasStaticVTable<sixtyfps_corelib::component::ComponentVTable> + HasWindow + 'static, X: vtable::HasStaticVTable<sixtyfps_corelib::component::ComponentVTable> + HasWindow + 'static,
Component: Into<vtable::VRc<sixtyfps_corelib::component::ComponentVTable, X>> + Clone, Component: Into<vtable::VRc<sixtyfps_corelib::component::ComponentVTable, X>> + ComponentHandle,
>( >(
component: &Component, component: &Component,
x: f32, x: f32,
y: f32, y: f32,
) { ) {
let rc = component.clone().into(); let rc = component.clone_strong().into();
let dyn_rc = vtable::VRc::into_dyn(rc.clone()); let dyn_rc = vtable::VRc::into_dyn(rc.clone());
sixtyfps_corelib::tests::sixtyfps_send_mouse_click(&dyn_rc, x, y, rc.component_window()); sixtyfps_corelib::tests::sixtyfps_send_mouse_click(&dyn_rc, x, y, rc.component_window());
} }
@ -322,7 +345,7 @@ pub mod testing {
/// Simulate a change in keyboard modifiers being pressed /// Simulate a change in keyboard modifiers being pressed
pub fn set_current_keyboard_modifiers< pub fn set_current_keyboard_modifiers<
X: vtable::HasStaticVTable<sixtyfps_corelib::component::ComponentVTable> + HasWindow, X: vtable::HasStaticVTable<sixtyfps_corelib::component::ComponentVTable> + HasWindow,
Component: Into<vtable::VRc<sixtyfps_corelib::component::ComponentVTable, X>> + Clone, Component: Into<vtable::VRc<sixtyfps_corelib::component::ComponentVTable, X>> + ComponentHandle,
>( >(
_component: &Component, _component: &Component,
modifiers: crate::re_exports::KeyboardModifiers, modifiers: crate::re_exports::KeyboardModifiers,
@ -333,12 +356,12 @@ pub mod testing {
/// Simulate entering a sequence of ascii characters key by key. /// Simulate entering a sequence of ascii characters key by key.
pub fn send_keyboard_string_sequence< pub fn send_keyboard_string_sequence<
X: vtable::HasStaticVTable<sixtyfps_corelib::component::ComponentVTable> + HasWindow, X: vtable::HasStaticVTable<sixtyfps_corelib::component::ComponentVTable> + HasWindow,
Component: Into<vtable::VRc<sixtyfps_corelib::component::ComponentVTable, X>> + Clone, Component: Into<vtable::VRc<sixtyfps_corelib::component::ComponentVTable, X>> + ComponentHandle,
>( >(
component: &Component, component: &Component,
sequence: &str, sequence: &str,
) { ) {
let component = component.clone().into(); let component = component.clone_strong().into();
sixtyfps_corelib::tests::send_keyboard_string_sequence( sixtyfps_corelib::tests::send_keyboard_string_sequence(
&super::SharedString::from(sequence), &super::SharedString::from(sequence),
KEYBOARD_MODIFIERS.with(|x| x.get()), KEYBOARD_MODIFIERS.with(|x| x.get()),
@ -349,12 +372,12 @@ pub mod testing {
/// Applies the specified rectangular constraints to the component's layout. /// Applies the specified rectangular constraints to the component's layout.
pub fn apply_layout< pub fn apply_layout<
X: vtable::HasStaticVTable<sixtyfps_corelib::component::ComponentVTable>, X: vtable::HasStaticVTable<sixtyfps_corelib::component::ComponentVTable>,
Component: Into<vtable::VRc<sixtyfps_corelib::component::ComponentVTable, X>> + Clone, Component: Into<vtable::VRc<sixtyfps_corelib::component::ComponentVTable, X>> + ComponentHandle,
>( >(
component: &Component, component: &Component,
rect: sixtyfps_corelib::graphics::Rect, rect: sixtyfps_corelib::graphics::Rect,
) { ) {
let rc = component.clone().into(); let rc = component.clone_strong().into();
vtable::VRc::borrow_pin(&rc).as_ref().apply_layout(rect); vtable::VRc::borrow_pin(&rc).as_ref().apply_layout(rect);
} }
@ -362,12 +385,12 @@ pub mod testing {
/// This overrides the value provided by the windowing system. /// This overrides the value provided by the windowing system.
pub fn set_window_scale_factor< pub fn set_window_scale_factor<
X: vtable::HasStaticVTable<sixtyfps_corelib::component::ComponentVTable> + HasWindow, X: vtable::HasStaticVTable<sixtyfps_corelib::component::ComponentVTable> + HasWindow,
Component: Into<vtable::VRc<sixtyfps_corelib::component::ComponentVTable, X>> + Clone, Component: Into<vtable::VRc<sixtyfps_corelib::component::ComponentVTable, X>> + ComponentHandle,
>( >(
component: &Component, component: &Component,
factor: f32, factor: f32,
) { ) {
let component = component.clone().into(); let component = component.clone_strong().into();
component.component_window().set_scale_factor(factor) component.component_window().set_scale_factor(factor)
} }
} }

View file

@ -35,7 +35,7 @@ pub fn main() {
main_window.set_fax_number(fax_number.into()); main_window.set_fax_number(fax_number.into());
}); });
let main_weak = main_window.clone().as_weak(); let main_weak = main_window.as_weak();
main_window.on_fax_send(move || { main_window.on_fax_send(move || {
let main_window = main_weak.upgrade().unwrap(); let main_window = main_weak.upgrade().unwrap();
let fax_number = main_window.get_fax_number().to_string(); let fax_number = main_window.get_fax_number().to_string();

View file

@ -111,7 +111,7 @@ pub fn generate(doc: &Document, diag: &mut BuildDiagnostics) -> Option<TokenStre
const _THE_SAME_VERSION_MUST_BE_USED_FOR_THE_COMPILER_AND_THE_RUNTIME : sixtyfps::#version_check = sixtyfps::#version_check; const _THE_SAME_VERSION_MUST_BE_USED_FOR_THE_COMPILER_AND_THE_RUNTIME : sixtyfps::#version_check = sixtyfps::#version_check;
} }
pub use #compo_module::{#compo_id #(,#structs_ids)* }; pub use #compo_module::{#compo_id #(,#structs_ids)* };
pub use sixtyfps::IntoWeak; pub use sixtyfps::ComponentHandle;
}) })
} }
@ -795,20 +795,35 @@ fn generate_component(
if !parent_component_type.is_empty() { Some(quote!(parent)) } else { None }; if !parent_component_type.is_empty() { Some(quote!(parent)) } else { None };
let window_parent_name = window_parent_param.as_ref().map(|_| quote!(, parent_window)); let window_parent_name = window_parent_param.as_ref().map(|_| quote!(, parent_window));
let run_fun = if component.parent_element.upgrade().is_none() { let component_handle_impl = if component.parent_element.upgrade().is_none() {
Some(quote!( Some(quote!(
pub fn run(&self) { impl sixtyfps::ComponentHandle for #public_component_id {
self.show(); type Inner = #inner_component_id;
sixtyfps::run_event_loop(); fn as_weak(&self) -> sixtyfps::Weak<Self> {
self.hide(); sixtyfps::Weak::new(&self.0)
} }
pub fn show(&self) { fn clone_strong(&self) -> Self {
vtable::VRc::as_pin_ref(&self.0).window.show(); Self(self.0.clone())
} }
pub fn hide(&self) { fn from_inner(inner: vtable::VRc<sixtyfps::re_exports::ComponentVTable, #inner_component_id>) -> Self {
vtable::VRc::as_pin_ref(&self.0).window.hide(); Self(inner)
}
fn run(&self) {
self.show();
sixtyfps::run_event_loop();
self.hide();
}
fn show(&self) {
vtable::VRc::as_pin_ref(&self.0).window.show();
}
fn hide(&self) {
vtable::VRc::as_pin_ref(&self.0).window.hide();
}
} }
)) ))
} else { } else {
@ -816,7 +831,6 @@ fn generate_component(
}; };
Some(quote!( Some(quote!(
#[derive(Clone)]
#visibility struct #public_component_id(vtable::VRc<sixtyfps::re_exports::ComponentVTable, #inner_component_id>); #visibility struct #public_component_id(vtable::VRc<sixtyfps::re_exports::ComponentVTable, #inner_component_id>);
impl #public_component_id { impl #public_component_id {
@ -824,20 +838,9 @@ fn generate_component(
Self(#inner_component_id::new(#parent_name #window_parent_name)) Self(#inner_component_id::new(#parent_name #window_parent_name))
} }
#(#property_and_callback_accessors)* #(#property_and_callback_accessors)*
#run_fun
} }
impl sixtyfps::IntoWeak for #public_component_id { #component_handle_impl
type Inner = #inner_component_id;
fn as_weak(&self) -> sixtyfps::Weak<Self> {
sixtyfps::Weak::new(&self.0)
}
fn from_inner(inner: vtable::VRc<sixtyfps::re_exports::ComponentVTable, #inner_component_id>) -> Self {
Self(inner)
}
}
impl From<#public_component_id> for vtable::VRc<sixtyfps::re_exports::ComponentVTable, #inner_component_id> { impl From<#public_component_id> for vtable::VRc<sixtyfps::re_exports::ComponentVTable, #inner_component_id> {
fn from(value: #public_component_id) -> Self { fn from(value: #public_component_id) -> Self {