Get rid of the context in properties/signal

This commit is contained in:
Olivier Goffart 2020-07-13 18:49:06 +02:00
parent ab7ae9f3e2
commit e00491811b
25 changed files with 389 additions and 653 deletions

View file

@ -46,7 +46,6 @@ private:
internal::ComponentWindowOpaque inner;
};
using internal::EvaluationContext;
using internal::Image;
using internal::Path;
using internal::Rectangle;
@ -69,14 +68,6 @@ constexpr inline ItemTreeNode<uint8_t> make_dyn_node(std::uintptr_t offset)
using internal::sixtyfps_visit_item_tree;
template<typename Component>
EvaluationContext evaluation_context_for_root_component(const Component *component) {
return EvaluationContext{
VRef<ComponentVTable> { &Component::component_type, const_cast<Component *>(component)},
nullptr,
};
}
// layouts:
using internal::Constraint;
using internal::GridLayoutCellData;

View file

@ -45,8 +45,8 @@ void Property<Color>::set_animated_binding(F binding,
{
internal::sixtyfps_property_set_animated_binding_color(
&inner,
[](void *user_data, const internal::EvaluationContext *context, Color *value) {
*reinterpret_cast<Color *>(value) = (*reinterpret_cast<F *>(user_data))(context);
[](void *user_data, Color *value) {
*reinterpret_cast<Color *>(value) = (*reinterpret_cast<F *>(user_data))();
},
new F(binding), [](void *user_data) { delete reinterpret_cast<F *>(user_data); },
&animation_data);

View file

@ -31,9 +31,9 @@ struct Property
internal::sixtyfps_property_set_changed(&inner);
}
const T &get(const internal::EvaluationContext *context) const
const T &get() const
{
internal::sixtyfps_property_update(&inner, context, &value);
internal::sixtyfps_property_update(&inner, &value);
return value;
}
@ -42,8 +42,8 @@ struct Property
{
internal::sixtyfps_property_set_binding(
&inner,
[](void *user_data, const internal::EvaluationContext *context, void *value) {
*reinterpret_cast<T *>(value) = (*reinterpret_cast<F *>(user_data))(context);
[](void *user_data, void *value) {
*reinterpret_cast<T *>(value) = (*reinterpret_cast<F *>(user_data))();
},
new F(binding), [](void *user_data) { delete reinterpret_cast<F *>(user_data); });
}
@ -79,8 +79,8 @@ void Property<int32_t>::set_animated_binding(F binding,
{
internal::sixtyfps_property_set_animated_binding_int(
&inner,
[](void *user_data, const internal::EvaluationContext *context, int32_t *value) {
*reinterpret_cast<int32_t *>(value) = (*reinterpret_cast<F *>(user_data))(context);
[](void *user_data, int32_t *value) {
*reinterpret_cast<int32_t *>(value) = (*reinterpret_cast<F *>(user_data))();
},
new F(binding), [](void *user_data) { delete reinterpret_cast<F *>(user_data); },
&animation_data);
@ -93,8 +93,8 @@ void Property<float>::set_animated_binding(F binding,
{
internal::sixtyfps_property_set_animated_binding_float(
&inner,
[](void *user_data, const internal::EvaluationContext *context, float *value) {
*reinterpret_cast<float *>(value) = (*reinterpret_cast<F *>(user_data))(context);
[](void *user_data, float *value) {
*reinterpret_cast<float *>(value) = (*reinterpret_cast<F *>(user_data))();
},
new F(binding), [](void *user_data) { delete reinterpret_cast<F *>(user_data); },
&animation_data);

View file

@ -18,15 +18,15 @@ struct Signal
{
internal::sixtyfps_signal_set_handler(
&inner,
[](void *user_data, const internal::EvaluationContext *value) {
(*reinterpret_cast<F *>(user_data))(value);
[](void *user_data) {
(*reinterpret_cast<F *>(user_data))();
},
new F(binding), [](void *user_data) { delete reinterpret_cast<F *>(user_data); });
}
void emit(const internal::EvaluationContext *context) const
void emit() const
{
internal::sixtyfps_signal_emit(&inner, context);
internal::sixtyfps_signal_emit(&inner);
}
private:

View file

@ -2,7 +2,7 @@ use core::cell::RefCell;
use neon::prelude::*;
use sixtyfps_compilerlib::typeregister::Type;
use sixtyfps_corelib::abi::datastructures::Resource;
use sixtyfps_corelib::{ComponentRefPin, EvaluationContext};
use sixtyfps_corelib::ComponentRefPin;
use std::rc::Rc;
@ -65,7 +65,7 @@ fn create<'cx>(
.set_signal_handler(
component.borrow_mut(),
prop_name.as_str(),
Box::new(move |_eval_ctx, ()| {
Box::new(move |()| {
GLOBAL_CONTEXT.with(|cx_fn| {
cx_fn(&move |cx, presistent_context| {
presistent_context
@ -243,7 +243,7 @@ declare_types! {
let x = this.borrow(&lock).0.clone();
let component = x.ok_or(()).or_else(|()| cx.throw_error("Invalid type"))?;
let value = component.description()
.get_property(&EvaluationContext::for_root_component(component.borrow()), prop_name.as_str())
.get_property(component.borrow(), prop_name.as_str())
.or_else(|_| cx.throw_error(format!("Cannot read property")))?;
to_js_value(value, &mut cx)
}
@ -275,7 +275,7 @@ declare_types! {
let x = this.borrow(&lock).0.clone();
let component = x.ok_or(()).or_else(|()| cx.throw_error("Invalid type"))?;
component.description()
.emit_signal(&EvaluationContext::for_root_component(component.borrow()), signal_name.as_str())
.emit_signal(component.borrow(), signal_name.as_str())
.or_else(|_| cx.throw_error(format!("Cannot emit signal")))?;
Ok(JsUndefined::new().as_value(&mut cx))

View file

@ -86,7 +86,6 @@ pub mod re_exports {
};
pub use sixtyfps_corelib::Color;
pub use sixtyfps_corelib::ComponentVTable_static;
pub use sixtyfps_corelib::EvaluationContext;
pub use sixtyfps_corelib::Resource;
pub use sixtyfps_corelib::SharedArray;
pub use sixtyfps_corelib::SharedString;

View file

@ -7,14 +7,14 @@ int main()
component.foobar.set_handler([](auto...) { std::cout << "Hello from C++" << std::endl; });
component.plus_clicked.set_handler([](auto ctx) {
component.plus_clicked.set_handler([]() {
auto &counter = component.counter;
counter.set(counter.get(ctx) + 1);
counter.set(counter.get() + 1);
});
component.minus_clicked.set_handler([](auto ctx) {
component.minus_clicked.set_handler([]() {
auto &counter = component.counter;
counter.set(counter.get(ctx) - 1);
counter.set(counter.get() - 1);
});
sixtyfps::ComponentWindow window;

View file

@ -128,15 +128,17 @@ Hello := Rectangle {
fn main() {
let app = Hello::new();
app.plus_clicked.set_handler(|context, ()| {
let app = context.get_component::<Hello>().unwrap();
let counter = Hello::field_offsets().counter.apply_pin(app);
counter.set(counter.get(context) + 1);
let app_weak = sixtyfps::re_exports::WeakPin::downgrade(app.clone());
app.plus_clicked.set_handler(move |()| {
let app = app_weak.upgrade().unwrap();
let counter = Hello::field_offsets().counter.apply_pin(app.as_ref());
counter.set(counter.get() + 1);
});
app.minus_clicked.set_handler(|context, ()| {
let app = context.get_component::<Hello>().unwrap();
let counter = Hello::field_offsets().counter.apply_pin(app);
counter.set(counter.get(context) - 1);
let app_weak = sixtyfps::re_exports::WeakPin::downgrade(app.clone());
app.minus_clicked.set_handler(move |()| {
let app = app_weak.upgrade().unwrap();
let counter = Hello::field_offsets().counter.apply_pin(app.as_ref());
counter.set(counter.get() - 1);
});
app.run();
}

View file

@ -4,16 +4,17 @@ sixtyfps::include_modules!();
fn main() {
let app = Hello::new();
app.plus_clicked.set_handler(|context, ()| {
let app = context.get_component::<Hello>().unwrap();
let counter = Hello::field_offsets().counter.apply_pin(app);
counter.set(counter.get(context) + 1);
let app_weak = sixtyfps::re_exports::WeakPin::downgrade(app.clone());
app.plus_clicked.set_handler(move |()| {
let app = app_weak.upgrade().unwrap();
let counter = Hello::field_offsets().counter.apply_pin(app.as_ref());
counter.set(counter.get() + 1);
});
app.minus_clicked.set_handler(|context, ()| {
let app = context.get_component::<Hello>().unwrap();
let counter = Hello::field_offsets().counter.apply_pin(app);
counter.set(counter.get(context) - 1);
let app_weak = sixtyfps::re_exports::WeakPin::downgrade(app.clone());
app.minus_clicked.set_handler(move |()| {
let app = app_weak.upgrade().unwrap();
let counter = Hello::field_offsets().counter.apply_pin(app.as_ref());
counter.set(counter.get() - 1);
});
app.run();
}

View file

@ -247,13 +247,12 @@ fn handle_item(item: &Element, main_struct: &mut Struct, init: &mut Vec<String>)
format!(
"{signal_accessor_prefix}{prop}.set_handler(
[]([[maybe_unused]] const sixtyfps::EvaluationContext *context) {{
[[maybe_unused]] auto self = reinterpret_cast<const {ty}*>(context->component.instance);
[this]() {{
[[maybe_unused]] auto self = this;
{code};
}});",
signal_accessor_prefix = signal_accessor_prefix,
prop = s,
ty = main_struct.name,
code = compile_expression(i, &item.enclosing_component.upgrade().unwrap())
)
} else {
@ -276,11 +275,10 @@ fn handle_item(item: &Element, main_struct: &mut Struct, init: &mut Vec<String>)
)
} else {
let binding_code = format!(
"[]([[maybe_unused]] const sixtyfps::EvaluationContext *context) {{
[[maybe_unused]] auto self = reinterpret_cast<const {ty}*>(context->component.instance);
"[this]() {{
[[maybe_unused]] auto self = this;
return {init};
}}",
ty = main_struct.name,
init = init
);
@ -385,10 +383,7 @@ fn generate_component(file: &mut File, component: &Rc<Component>, diag: &mut Dia
for (cpp_name, property_decl) in component.root_element.borrow().property_declarations.iter() {
let ty = if property_decl.property_type == Type::Signal {
if property_decl.expose_in_public_api && is_root {
let signal_emitter: Vec<String> = vec![
"[[maybe_unused]] auto context = sixtyfps::evaluation_context_for_root_component(this);".into(),
format!("{}.emit(&context);", cpp_name)
];
let signal_emitter: Vec<String> = vec![format!("{}.emit();", cpp_name)];
component_struct.members.push(Declaration::Function(Function {
name: format!("emit_{}", cpp_name),
@ -411,10 +406,7 @@ fn generate_component(file: &mut File, component: &Rc<Component>, diag: &mut Dia
});
if property_decl.expose_in_public_api && is_root {
let prop_getter: Vec<String> = vec![
"[[maybe_unused]] auto context = sixtyfps::evaluation_context_for_root_component(this);".into(),
format!("return {}.get(&context);", cpp_name)
];
let prop_getter: Vec<String> = vec![format!("return {}.get();", cpp_name)];
component_struct.members.push(Declaration::Function(Function {
name: format!("get_{}", cpp_name),
@ -567,9 +559,7 @@ fn generate_component(file: &mut File, component: &Rc<Component>, diag: &mut Dia
component_struct.members.push(Declaration::Function(Function {
name: "compute_layout".into(),
signature:
"(sixtyfps::ComponentRef component, [[maybe_unused]] const sixtyfps::EvaluationContext *context) -> void"
.into(),
signature: "(sixtyfps::ComponentRef component) -> void".into(),
is_static: true,
statements: Some(compute_layout(component)),
..Default::default()
@ -601,8 +591,6 @@ fn generate_component(file: &mut File, component: &Rc<Component>, diag: &mut Dia
"static const auto dyn_visit = [] (const uint8_t *base, [[maybe_unused]] sixtyfps::ItemVisitorRefMut visitor, uintptr_t dyn_index) {".to_owned(),
format!(" [[maybe_unused]] auto self = reinterpret_cast<const {}*>(base);", component_id),
// Fixme: this is not the root component
"auto context_ = sixtyfps::evaluation_context_for_root_component(self);".into(),
"[[maybe_unused]] auto context = &context_;".into(),
format!(" switch(dyn_index) {{ {} }}\n }};", children_visitor_case.join("")),
"return sixtyfps::sixtyfps_visit_item_tree(component, { const_cast<sixtyfps::ItemTreeNode<uint8_t>*>(children), std::size(children)}, index, visitor, dyn_visit);".to_owned(),
]),
@ -627,28 +615,27 @@ fn component_id(component: &Rc<Component>) -> String {
}
}
/// Returns the code that can access the given property or signal from the context (but without the set or get)
/// Returns the code that can access the given property (but without the set or get)
///
/// to be used like:
/// ```ignore
/// let (access, context) = access_member(...)
/// format!("context.{access}.get({context})", ...)
/// let access = access_member(...);
/// format!("{}.get()", access)
/// ```
fn access_member(
element: &ElementRc,
name: &str,
component: &Rc<Component>,
context: &str,
component_cpp: &str,
) -> (String, String) {
) -> String {
let e = element.borrow();
let enclosing_component = e.enclosing_component.upgrade().unwrap();
if Rc::ptr_eq(component, &enclosing_component) {
let e = element.borrow();
if e.property_declarations.contains_key(name) {
(format!("{}->{}", component_cpp, name), context.into())
format!("{}->{}", component_cpp, name)
} else {
(format!("{}->{}.{}", component_cpp, e.id.as_str(), name), context.into())
format!("{}->{}.{}", component_cpp, e.id.as_str(), name)
}
} else {
access_member(
@ -662,7 +649,6 @@ fn access_member(
.enclosing_component
.upgrade()
.unwrap(),
&format!("{}->parent_context", context),
&format!("{}->parent", component_cpp),
)
}
@ -676,35 +662,25 @@ fn compile_expression(e: &crate::expression_tree::Expression, component: &Rc<Com
NumberLiteral(n) => n.to_string(),
BoolLiteral(b) => b.to_string(),
PropertyReference(NamedReference { element, name }) => {
let (access, context) = access_member(
&element.upgrade().unwrap(),
name.as_str(),
component,
"context",
"self",
);
format!(r#"{}.get({})"#, access, context)
let access =
access_member(&element.upgrade().unwrap(), name.as_str(), component, "self");
format!(r#"{}.get()"#, access)
}
SignalReference(NamedReference { element, name }) => {
let (access, context) = access_member(
&element.upgrade().unwrap(),
name.as_str(),
component,
"context",
"self",
);
format!(r#"{}.emit({})"#, access, context)
let access =
access_member(&element.upgrade().unwrap(), name.as_str(), component, "self");
format!(r#"{}.emit()"#, access)
}
RepeaterIndexReference { element } => {
if element.upgrade().unwrap().borrow().base_type == Type::Component(component.clone()) {
"self->index.get(context)".to_owned()
"self->index.get()".to_owned()
} else {
todo!();
}
}
RepeaterModelReference { element } => {
if element.upgrade().unwrap().borrow().base_type == Type::Component(component.clone()) {
"self->model_data.get(context)".to_owned()
"self->model_data.get()".to_owned()
} else {
todo!();
}
@ -748,19 +724,13 @@ fn compile_expression(e: &crate::expression_tree::Expression, component: &Rc<Com
}
SelfAssignment { lhs, rhs, op } => match &**lhs {
PropertyReference(NamedReference { element, name }) => {
let (access, context) = access_member(
&element.upgrade().unwrap(),
name.as_str(),
component,
"context",
"self",
);
let access =
access_member(&element.upgrade().unwrap(), name.as_str(), component, "self");
format!(
r#"{lhs}.set({lhs}.get({context}) {op} {rhs})"#,
r#"{lhs}.set({lhs}.get() {op} {rhs})"#,
lhs = access,
rhs = compile_expression(&*rhs, component),
op = op,
context = context
)
}
_ => panic!("typechecking should make sure this was a PropertyReference"),
@ -894,8 +864,8 @@ fn compute_layout(component: &Rc<Component>) -> Vec<String> {
// FIXME: add auto conversion from std::array* to Slice
res.push(" { row_constr.data(), row_constr.size() },".to_owned());
res.push(" { col_constr.data(), col_constr.size() },".to_owned());
res.push(format!(" self->{}.width.get(context),", grid.within.borrow().id));
res.push(format!(" self->{}.height.get(context),", grid.within.borrow().id));
res.push(format!(" self->{}.width.get(),", grid.within.borrow().id));
res.push(format!(" self->{}.height.get(),", grid.within.borrow().id));
res.push(" x, y,".to_owned());
res.push(" {cells, std::size(cells)}".to_owned());
res.push(" };".to_owned());

View file

@ -52,10 +52,7 @@ pub fn generate(component: &Rc<Component>, diag: &mut Diagnostics) -> Option<Tok
quote!(
#[allow(dead_code)]
fn #emitter_ident(self: ::core::pin::Pin<&Self>) {
let eval_context = sixtyfps::re_exports::EvaluationContext::for_root_component(
sixtyfps::re_exports::ComponentRef::new_pin(self)
);
Self::field_offsets().#prop_ident.apply_pin(self).emit(&eval_context, ())
Self::field_offsets().#prop_ident.apply_pin(self).emit(())
}
)
.into(),
@ -79,10 +76,7 @@ pub fn generate(component: &Rc<Component>, diag: &mut Diagnostics) -> Option<Tok
quote!(
#[allow(dead_code)]
fn #getter_ident(self: ::core::pin::Pin<&Self>) -> #rust_property_type {
let eval_context = sixtyfps::re_exports::EvaluationContext::for_root_component(
sixtyfps::re_exports::ComponentRef::new_pin(self)
);
Self::field_offsets().#prop_ident.apply_pin(self).get(&eval_context)
Self::field_offsets().#prop_ident.apply_pin(self).get()
}
)
.into(),
@ -180,11 +174,6 @@ pub fn generate(component: &Rc<Component>, diag: &mut Diagnostics) -> Option<Tok
if self_pinned.#model_name.is_dirty() {
#component_id::field_offsets().#model_name.apply_pin(self_pinned).evaluate(|| {
let _self = self_pinned.clone();
// FIXME: this should not be the root_component (but that's fine as we no longer access the parent)
let context = sixtyfps::re_exports::EvaluationContext::for_root_component(
sixtyfps::re_exports::ComponentRef::new_pin(_self)
);
let context = &context;
self_pinned.#repeater_id.update_model(#model, || {
#rep_component_id::new(self_pinned.self_weak.get().unwrap().clone())
});
@ -226,9 +215,13 @@ pub fn generate(component: &Rc<Component>, diag: &mut Diagnostics) -> Option<Tok
if matches!(item.lookup_property(k.as_str()), Type::Signal) {
init.push(quote!(
self_pinned.#rust_property.set_handler(|context, ()| {
let _self = context.get_component::<#component_id>().unwrap();
self_pinned.#rust_property.set_handler({
let self_weak = sixtyfps::re_exports::WeakPin::downgrade(self_pinned.clone());
move |()| {
let self_pinned = self_weak.upgrade().unwrap();
let _self = self_pinned.as_ref();
#tokens_for_expression;
}
});
));
} else {
@ -246,7 +239,7 @@ pub fn generate(component: &Rc<Component>, diag: &mut Diagnostics) -> Option<Tok
k,
quote!({
let self_weak = sixtyfps::re_exports::WeakPin::downgrade(self_pinned.clone());
move |context| {
move || {
let self_pinned = self_weak.upgrade().unwrap();
let _self = self_pinned.as_ref();
(#tokens_for_expression) as _
@ -450,20 +443,19 @@ fn property_set_binding_tokens(
}
}
/// Returns the code that can access the given property or signal from the context (but without the set or get)
/// Returns the code that can access the given property or signal (but without the set or get)
///
/// to be used like:
/// ```ignore
/// let (access, context) = access_member(...)
/// quote!(#access.get(#context))
/// let access = access_member(...)
/// quote!(#access.get())
/// ```
fn access_member(
element: &ElementRc,
name: &str,
component: &Rc<Component>,
context: TokenStream,
component_rust: TokenStream,
) -> (TokenStream, TokenStream) {
) -> TokenStream {
let e = element.borrow();
let enclosing_component = e.enclosing_component.upgrade().unwrap();
@ -471,15 +463,13 @@ fn access_member(
let component_id = component_id(&enclosing_component);
let name_ident = quote::format_ident!("{}", name);
if e.property_declarations.contains_key(name) {
(quote!(#component_id::field_offsets().#name_ident.apply_pin(#component_rust)), context)
quote!(#component_id::field_offsets().#name_ident.apply_pin(#component_rust))
} else {
let elem_ident = quote::format_ident!("{}", e.id);
let elem_ty = quote::format_ident!("{}", e.base_type.as_builtin().class_name);
(
quote!((#component_id::field_offsets().#elem_ident + #elem_ty::field_offsets().#name_ident)
.apply_pin(#component_rust)
),
context,
)
}
} else {
@ -494,7 +484,6 @@ fn access_member(
.enclosing_component
.upgrade()
.unwrap(),
quote!((&sixtyfps::re_exports::EvaluationContext::for_root_component(#context.component))),
quote!(#component_rust.parent.upgrade().unwrap().as_ref()),
)
}
@ -520,19 +509,14 @@ fn compile_expression(e: &Expression, component: &Rc<Component>) -> TokenStream
}
}
Expression::PropertyReference(NamedReference { element, name }) => {
let (access, context) = access_member(
&element.upgrade().unwrap(),
name.as_str(),
component,
quote!(context),
quote!(_self),
);
quote!(#access.get(#context))
let access =
access_member(&element.upgrade().unwrap(), name.as_str(), component, quote!(_self));
quote!(#access.get())
}
Expression::RepeaterIndexReference { element } => {
if element.upgrade().unwrap().borrow().base_type == Type::Component(component.clone()) {
let component_id = component_id(&component);
quote!({ #component_id::field_offsets().index.apply_pin(_self).get(context) })
quote!({ #component_id::field_offsets().index.apply_pin(_self).get() })
} else {
todo!();
}
@ -540,7 +524,7 @@ fn compile_expression(e: &Expression, component: &Rc<Component>) -> TokenStream
Expression::RepeaterModelReference { element } => {
if element.upgrade().unwrap().borrow().base_type == Type::Component(component.clone()) {
let component_id = component_id(&component);
quote!({ #component_id::field_offsets().model_data.apply_pin(_self).get(context) })
quote!({ #component_id::field_offsets().model_data.apply_pin(_self).get() })
} else {
todo!();
}
@ -562,14 +546,9 @@ fn compile_expression(e: &Expression, component: &Rc<Component>) -> TokenStream
quote!({ #(#map);* })
}
Expression::SignalReference(NamedReference { element, name, .. }) => {
let (access, context) = access_member(
&element.upgrade().unwrap(),
name.as_str(),
component,
quote!(context),
quote!(_self),
);
quote!(#access.emit(#context, ()))
let access =
access_member(&element.upgrade().unwrap(), name.as_str(), component, quote!(_self));
quote!(#access.emit(()))
}
Expression::FunctionCall { function } => {
if matches!(function.ty(), Type::Signal) {
@ -581,16 +560,15 @@ fn compile_expression(e: &Expression, component: &Rc<Component>) -> TokenStream
}
Expression::SelfAssignment { lhs, rhs, op } => match &**lhs {
Expression::PropertyReference(NamedReference { element, name }) => {
let (lhs, context) = access_member(
let lhs = access_member(
&element.upgrade().unwrap(),
name.as_str(),
component,
quote!(context),
quote!(_self),
);
let rhs = compile_expression(&*rhs, &component);
let op = proc_macro2::Punct::new(*op, proc_macro2::Spacing::Alone);
quote!( #lhs.set(#lhs.get(#context) #op &((#rhs) as _) ))
quote!( #lhs.set(#lhs.get() #op &((#rhs) as _) ))
}
_ => panic!("typechecking should make sure this was a PropertyReference"),
},
@ -725,9 +703,9 @@ fn compute_layout(component: &Rc<Component>) -> TokenStream {
row_constraint: Slice::from_slice(&[#(#row_constraint),*]),
col_constraint: Slice::from_slice(&[#(#col_constraint),*]),
width: (Self::field_offsets().#within + #within_ty::field_offsets().width)
.apply_pin(self).get(context),
.apply_pin(self).get(),
height: (Self::field_offsets().#within + #within_ty::field_offsets().height)
.apply_pin(self).get(context),
.apply_pin(self).get(),
x: #x_pos,
y: #y_pos,
cells: Slice::from_slice(&[#( Slice::from_slice(&[#( #cells ),*])),*]),
@ -781,7 +759,7 @@ fn compute_layout(component: &Rc<Component>) -> TokenStream {
fn layout_info(self: ::core::pin::Pin<&Self>) -> sixtyfps::re_exports::LayoutInfo {
todo!("Implement in rust.rs")
}
fn compute_layout(self: ::core::pin::Pin<&Self>, context: &sixtyfps::re_exports::EvaluationContext) {
fn compute_layout(self: ::core::pin::Pin<&Self>) {
#![allow(unused)]
use sixtyfps::re_exports::*;
let dummy = Property::<f32>::default();

View file

@ -1,7 +1,6 @@
//! This module contains the basic datastructures that are exposed to the C API
use super::slice::Slice;
use crate::EvaluationContext;
use core::pin::Pin;
use std::cell::Cell;
use vtable::*;
@ -53,8 +52,7 @@ pub struct ComponentVTable {
pub layout_info: extern "C" fn(core::pin::Pin<VRef<ComponentVTable>>) -> LayoutInfo,
/// Will compute the layout of
pub compute_layout:
extern "C" fn(core::pin::Pin<VRef<ComponentVTable>>, eval_context: &EvaluationContext),
pub compute_layout: extern "C" fn(core::pin::Pin<VRef<ComponentVTable>>),
}
/// This structure must be present in items that are Rendered and contains information.
@ -111,8 +109,7 @@ pub enum ItemTreeNode<T> {
#[repr(C)]
pub struct ItemVTable {
/// Returns the geometry of this item (relative to its parent item)
pub geometry:
extern "C" fn(core::pin::Pin<VRef<ItemVTable>>, context: &crate::EvaluationContext) -> Rect,
pub geometry: extern "C" fn(core::pin::Pin<VRef<ItemVTable>>) -> Rect,
/// offset in bytes fromthe *const ItemImpl.
/// isize::MAX means None
@ -121,17 +118,13 @@ pub struct ItemVTable {
pub cached_rendering_data_offset: usize,
/// Return the rendering primitive used to display this item.
pub rendering_primitive: extern "C" fn(
core::pin::Pin<VRef<ItemVTable>>,
context: &crate::EvaluationContext,
) -> RenderingPrimitive,
pub rendering_primitive: extern "C" fn(core::pin::Pin<VRef<ItemVTable>>) -> RenderingPrimitive,
/// We would need max/min/preferred size, and all layout info
pub layouting_info: extern "C" fn(core::pin::Pin<VRef<ItemVTable>>) -> LayoutInfo,
/// input event
pub input_event:
extern "C" fn(core::pin::Pin<VRef<ItemVTable>>, MouseEvent, &crate::EvaluationContext),
pub input_event: extern "C" fn(core::pin::Pin<VRef<ItemVTable>>, MouseEvent),
}
/// The constraint that applies to an item

View file

@ -21,7 +21,7 @@ use super::datastructures::{
};
#[cfg(feature = "rtti")]
use crate::rtti::*;
use crate::{EvaluationContext, Property, SharedString, Signal};
use crate::{Property, SharedString, Signal};
use const_field_offset::FieldOffsets;
use core::pin::Pin;
use corelib_macro::*;
@ -40,27 +40,24 @@ pub struct Rectangle {
}
impl Item for Rectangle {
fn geometry(self: Pin<&Self>, context: &EvaluationContext) -> Rect {
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(
Self::field_offsets().x.apply_pin(self).get(context),
Self::field_offsets().y.apply_pin(self).get(context),
Self::field_offsets().width.apply_pin(self).get(context),
Self::field_offsets().height.apply_pin(self).get(context),
Self::field_offsets().x.apply_pin(self).get(),
Self::field_offsets().y.apply_pin(self).get(),
Self::field_offsets().width.apply_pin(self).get(),
Self::field_offsets().height.apply_pin(self).get(),
)
}
fn rendering_primitive(
self: Pin<&Self>,
context: &crate::EvaluationContext,
) -> RenderingPrimitive {
let width = Self::field_offsets().width.apply_pin(self).get(context);
let height = Self::field_offsets().height.apply_pin(self).get(context);
fn rendering_primitive(self: Pin<&Self>) -> RenderingPrimitive {
let width = Self::field_offsets().width.apply_pin(self).get();
let height = Self::field_offsets().height.apply_pin(self).get();
if width > 0. && height > 0. {
RenderingPrimitive::Rectangle {
x: Self::field_offsets().x.apply_pin(self).get(context),
y: Self::field_offsets().y.apply_pin(self).get(context),
x: Self::field_offsets().x.apply_pin(self).get(),
y: Self::field_offsets().y.apply_pin(self).get(),
width,
height,
color: Self::field_offsets().color.apply_pin(self).get(context),
color: Self::field_offsets().color.apply_pin(self).get(),
}
} else {
RenderingPrimitive::NoContents
@ -71,12 +68,7 @@ impl Item for Rectangle {
Default::default()
}
fn input_event(
self: Pin<&Self>,
_: super::datastructures::MouseEvent,
_: &crate::EvaluationContext,
) {
}
fn input_event(self: Pin<&Self>, _: super::datastructures::MouseEvent) {}
}
impl ItemConsts for Rectangle {
@ -102,22 +94,19 @@ pub struct Image {
}
impl Item for Image {
fn geometry(self: Pin<&Self>, context: &crate::EvaluationContext) -> Rect {
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(
Self::field_offsets().x.apply_pin(self).get(context),
Self::field_offsets().y.apply_pin(self).get(context),
Self::field_offsets().width.apply_pin(self).get(context),
Self::field_offsets().height.apply_pin(self).get(context),
Self::field_offsets().x.apply_pin(self).get(),
Self::field_offsets().y.apply_pin(self).get(),
Self::field_offsets().width.apply_pin(self).get(),
Self::field_offsets().height.apply_pin(self).get(),
)
}
fn rendering_primitive(
self: Pin<&Self>,
context: &crate::EvaluationContext,
) -> RenderingPrimitive {
fn rendering_primitive(self: Pin<&Self>) -> RenderingPrimitive {
RenderingPrimitive::Image {
x: Self::field_offsets().x.apply_pin(self).get(context),
y: Self::field_offsets().y.apply_pin(self).get(context),
source: Self::field_offsets().source.apply_pin(self).get(context),
x: Self::field_offsets().x.apply_pin(self).get(),
y: Self::field_offsets().y.apply_pin(self).get(),
source: Self::field_offsets().source.apply_pin(self).get(),
}
}
@ -126,12 +115,7 @@ impl Item for Image {
Default::default()
}
fn input_event(
self: Pin<&Self>,
_: super::datastructures::MouseEvent,
_: &crate::EvaluationContext,
) {
}
fn input_event(self: Pin<&Self>, _: super::datastructures::MouseEvent) {}
}
impl ItemConsts for Image {
@ -159,25 +143,22 @@ pub struct Text {
impl Item for Text {
// FIXME: width / height. or maybe it doesn't matter? (
fn geometry(self: Pin<&Self>, context: &crate::EvaluationContext) -> Rect {
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(
Self::field_offsets().x.apply_pin(self).get(context),
Self::field_offsets().y.apply_pin(self).get(context),
Self::field_offsets().x.apply_pin(self).get(),
Self::field_offsets().y.apply_pin(self).get(),
0.,
0.,
)
}
fn rendering_primitive(
self: Pin<&Self>,
context: &crate::EvaluationContext,
) -> RenderingPrimitive {
fn rendering_primitive(self: Pin<&Self>) -> RenderingPrimitive {
RenderingPrimitive::Text {
x: Self::field_offsets().x.apply_pin(self).get(context),
y: Self::field_offsets().y.apply_pin(self).get(context),
text: Self::field_offsets().text.apply_pin(self).get(context),
font_family: Self::field_offsets().font_family.apply_pin(self).get(context),
font_pixel_size: Self::field_offsets().font_pixel_size.apply_pin(self).get(context),
color: Self::field_offsets().color.apply_pin(self).get(context),
x: Self::field_offsets().x.apply_pin(self).get(),
y: Self::field_offsets().y.apply_pin(self).get(),
text: Self::field_offsets().text.apply_pin(self).get(),
font_family: Self::field_offsets().font_family.apply_pin(self).get(),
font_pixel_size: Self::field_offsets().font_pixel_size.apply_pin(self).get(),
color: Self::field_offsets().color.apply_pin(self).get(),
}
}
@ -185,12 +166,7 @@ impl Item for Text {
todo!()
}
fn input_event(
self: Pin<&Self>,
_: super::datastructures::MouseEvent,
_: &crate::EvaluationContext,
) {
}
fn input_event(self: Pin<&Self>, _: super::datastructures::MouseEvent) {}
}
impl ItemConsts for Text {
@ -217,18 +193,15 @@ pub struct TouchArea {
}
impl Item for TouchArea {
fn geometry(self: Pin<&Self>, context: &crate::EvaluationContext) -> Rect {
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(
Self::field_offsets().x.apply_pin(self).get(context),
Self::field_offsets().y.apply_pin(self).get(context),
Self::field_offsets().width.apply_pin(self).get(context),
Self::field_offsets().height.apply_pin(self).get(context),
Self::field_offsets().x.apply_pin(self).get(),
Self::field_offsets().y.apply_pin(self).get(),
Self::field_offsets().width.apply_pin(self).get(),
Self::field_offsets().height.apply_pin(self).get(),
)
}
fn rendering_primitive(
self: Pin<&Self>,
_context: &crate::EvaluationContext,
) -> RenderingPrimitive {
fn rendering_primitive(self: Pin<&Self>) -> RenderingPrimitive {
RenderingPrimitive::NoContents
}
@ -236,11 +209,7 @@ impl Item for TouchArea {
todo!()
}
fn input_event(
self: Pin<&Self>,
event: super::datastructures::MouseEvent,
context: &crate::EvaluationContext,
) {
fn input_event(self: Pin<&Self>, event: super::datastructures::MouseEvent) {
println!("Touch Area Event {:?}", event);
Self::field_offsets().pressed.apply_pin(self).set(match event.what {
super::datastructures::MouseEventType::MousePressed => true,
@ -248,7 +217,7 @@ impl Item for TouchArea {
super::datastructures::MouseEventType::MouseMoved => return,
});
if matches!(event.what, super::datastructures::MouseEventType::MouseReleased) {
Self::field_offsets().clicked.apply_pin(self).emit(context, ())
Self::field_offsets().clicked.apply_pin(self).emit(())
}
}
}
@ -278,27 +247,24 @@ pub struct Path {
}
impl Item for Path {
fn geometry(self: Pin<&Self>, context: &crate::EvaluationContext) -> Rect {
fn geometry(self: Pin<&Self>) -> Rect {
euclid::rect(
Self::field_offsets().x.apply_pin(self).get(context),
Self::field_offsets().y.apply_pin(self).get(context),
Self::field_offsets().x.apply_pin(self).get(),
Self::field_offsets().y.apply_pin(self).get(),
0.,
0.,
)
}
fn rendering_primitive(
self: Pin<&Self>,
context: &crate::EvaluationContext,
) -> RenderingPrimitive {
fn rendering_primitive(self: Pin<&Self>) -> RenderingPrimitive {
RenderingPrimitive::Path {
x: Self::field_offsets().x.apply_pin(self).get(context),
y: Self::field_offsets().y.apply_pin(self).get(context),
width: Self::field_offsets().width.apply_pin(self).get(context),
height: Self::field_offsets().height.apply_pin(self).get(context),
elements: Self::field_offsets().elements.apply_pin(self).get(context),
fill_color: Self::field_offsets().fill_color.apply_pin(self).get(context),
stroke_color: Self::field_offsets().stroke_color.apply_pin(self).get(context),
stroke_width: Self::field_offsets().stroke_width.apply_pin(self).get(context),
x: Self::field_offsets().x.apply_pin(self).get(),
y: Self::field_offsets().y.apply_pin(self).get(),
width: Self::field_offsets().width.apply_pin(self).get(),
height: Self::field_offsets().height.apply_pin(self).get(),
elements: Self::field_offsets().elements.apply_pin(self).get(),
fill_color: Self::field_offsets().fill_color.apply_pin(self).get(),
stroke_color: Self::field_offsets().stroke_color.apply_pin(self).get(),
stroke_width: Self::field_offsets().stroke_width.apply_pin(self).get(),
}
}
@ -306,12 +272,7 @@ impl Item for Path {
todo!()
}
fn input_event(
self: Pin<&Self>,
_: super::datastructures::MouseEvent,
_: &crate::EvaluationContext,
) {
}
fn input_event(self: Pin<&Self>, _: super::datastructures::MouseEvent) {}
}
impl ItemConsts for Path {

View file

@ -66,45 +66,6 @@ use core::{marker::PhantomPinned, pin::Pin};
use crate::abi::datastructures::Color;
use crate::abi::primitives::PropertyAnimation;
use crate::ComponentRefPin;
/// This structure contains what is required for the property engine to evaluate properties
///
/// One must pass it to the getter of the property, or emit of signals, and it can
/// be accessed from the bindings
#[repr(C)]
pub struct EvaluationContext<'a> {
/// The component which contains the Property or the Signal
pub component: core::pin::Pin<vtable::VRef<'a, crate::abi::datastructures::ComponentVTable>>,
/// The context of the parent component
pub parent_context: Option<&'a EvaluationContext<'a>>,
}
impl<'a> EvaluationContext<'a> {
/// Create a new context related to the root component
///
/// The component need to be a root component, otherwise fetching properties
/// might panic.
pub fn for_root_component(component: ComponentRefPin<'a>) -> Self {
Self { component, parent_context: None }
}
/// Create a context for a child component of a component within the current
/// context.
pub fn child_context(&'a self, child: ComponentRefPin<'a>) -> Self {
Self { component: child, parent_context: Some(self) }
}
/// Attempt to cast the component to the given type
pub fn get_component<
T: vtable::HasStaticVTable<crate::abi::datastructures::ComponentVTable>,
>(
&'a self,
) -> Option<core::pin::Pin<&'a T>> {
vtable::VRef::downcast_pin(self.component)
}
}
/// The return value of a binding
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
@ -118,11 +79,7 @@ enum BindingResult {
struct BindingVTable {
drop: unsafe fn(_self: *mut BindingHolder),
evaluate: unsafe fn(
_self: *mut BindingHolder,
value: *mut (),
context: &EvaluationContext,
) -> BindingResult,
evaluate: unsafe fn(_self: *mut BindingHolder, value: *mut ()) -> BindingResult,
mark_dirty: unsafe fn(_self: *const BindingHolder),
}
@ -130,24 +87,16 @@ struct BindingVTable {
trait BindingCallable {
/// This function is called by the property to evaluate the binding and produce a new value. The
/// previous property value is provided in the value parameter.
unsafe fn evaluate(
self: Pin<&Self>,
value: *mut (),
context: &EvaluationContext,
) -> BindingResult;
unsafe fn evaluate(self: Pin<&Self>, value: *mut ()) -> BindingResult;
/// This function is used to notify the binding that one of the dependencies was changed
/// and therefore this binding may evaluate to a different value, too.
fn mark_dirty(self: Pin<&Self>) {}
}
impl<F: Fn(*mut (), &EvaluationContext) -> BindingResult> BindingCallable for F {
unsafe fn evaluate(
self: Pin<&Self>,
value: *mut (),
context: &EvaluationContext,
) -> BindingResult {
self(value, context)
impl<F: Fn(*mut ()) -> BindingResult> BindingCallable for F {
unsafe fn evaluate(self: Pin<&Self>, value: *mut ()) -> BindingResult {
self(value)
}
}
@ -178,12 +127,10 @@ fn alloc_binding_holder<B: BindingCallable + 'static>(binding: B) -> *mut Bindin
unsafe fn evaluate<B: BindingCallable>(
_self: *mut BindingHolder,
value: *mut (),
context: &EvaluationContext,
) -> BindingResult {
let pinned_holder = Pin::new_unchecked(&*_self);
CURRENT_BINDING.set(pinned_holder, || {
Pin::new_unchecked(&((*(_self as *mut BindingHolder<B>)).binding))
.evaluate(value, context)
Pin::new_unchecked(&((*(_self as *mut BindingHolder<B>)).binding)).evaluate(value)
})
}
@ -384,7 +331,7 @@ impl PropertyHandle {
// `value` is the content of the unsafe cell and will be only dereferenced if the
// handle is not locked. (Upholding the requirements of UnsafeCell)
unsafe fn update<T>(&self, value: *mut T, context: &EvaluationContext) {
unsafe fn update<T>(&self, value: *mut T) {
let remove = self.access(|binding| {
if let Some(mut binding) = binding {
if binding.dirty.get() {
@ -393,7 +340,6 @@ impl PropertyHandle {
let r = (binding.vtable.evaluate)(
binding.as_mut().get_unchecked_mut() as *mut BindingHolder,
value as *mut (),
context,
);
binding.dirty.set(false);
if r == BindingResult::RemoveBinding {
@ -489,8 +435,8 @@ impl<T: Clone> Property<T> {
///
/// Panics if this property is get while evaluating its own binding or
/// cloning the value.
pub fn get(self: Pin<&Self>, context: &EvaluationContext) -> T {
unsafe { self.handle.update(self.value.get(), context) };
pub fn get(self: Pin<&Self>) -> T {
unsafe { self.handle.update(self.value.get()) };
self.handle.register_as_dependency_to_current_binding();
self.get_internal()
}
@ -522,10 +468,10 @@ impl<T: Clone> Property<T> {
///
/// If other properties have bindings depending of this property, these properties will
/// be marked as dirty.
//FIXME pub fn set_binding(self: Pin<&Self>, f: impl (Fn(&EvaluationContext) -> T) + 'static) {
pub fn set_binding(&self, f: impl (Fn(&EvaluationContext) -> T) + 'static) {
self.handle.set_binding(move |val: *mut (), context: &EvaluationContext| unsafe {
*(val as *mut T) = f(context);
//FIXME pub fn set_binding(self: Pin<&Self>, f: impl (Fn() -> T) + 'static) {
pub fn set_binding(&self, f: impl (Fn() -> T) + 'static) {
self.handle.set_binding(move |val: *mut ()| unsafe {
*(val as *mut T) = f();
BindingResult::KeepBinding
});
self.handle.mark_dirty();
@ -542,7 +488,7 @@ impl<T: Clone + InterpolatedPropertyValue + 'static> Property<T> {
pub fn set_animated_value(&self, value: T, animation_data: &PropertyAnimation) {
// FIXME if the current value is a dirty binding, we must run it, but we do not have the context
let d = PropertyValueAnimationData::new(self.get_internal(), value, animation_data.clone());
self.handle.set_binding(move |val: *mut (), _context: &EvaluationContext| unsafe {
self.handle.set_binding(move |val: *mut ()| unsafe {
let (value, finished) = d.compute_interpolated_value();
*(val as *mut T) = value;
if finished {
@ -559,14 +505,14 @@ impl<T: Clone + InterpolatedPropertyValue + 'static> Property<T> {
///
pub fn set_animated_binding(
&self,
f: impl (Fn(&EvaluationContext) -> T) + 'static,
f: impl (Fn() -> T) + 'static,
animation_data: &PropertyAnimation,
) {
self.handle.set_binding(AnimatedBindingCallable::<T> {
original_binding: PropertyHandle {
handle: Cell::new(
(alloc_binding_holder(move |val: *mut (), context: &EvaluationContext| unsafe {
*(val as *mut T) = f(context);
(alloc_binding_holder(move |val: *mut ()| unsafe {
*(val as *mut T) = f();
BindingResult::KeepBinding
}) as usize)
| 0b10,
@ -626,11 +572,7 @@ struct AnimatedBindingCallable<T> {
}
impl<T: InterpolatedPropertyValue> BindingCallable for AnimatedBindingCallable<T> {
unsafe fn evaluate(
self: Pin<&Self>,
value: *mut (),
context: &EvaluationContext,
) -> BindingResult {
unsafe fn evaluate(self: Pin<&Self>, value: *mut ()) -> BindingResult {
self.original_binding.register_as_dependency_to_current_binding();
match self.state.get() {
AnimatedBindingState::Animating => {
@ -644,15 +586,14 @@ impl<T: InterpolatedPropertyValue> BindingCallable for AnimatedBindingCallable<T
}
}
AnimatedBindingState::NotAnimating => {
self.original_binding.update(value, context);
self.original_binding.update(value);
}
AnimatedBindingState::ShouldStart => {
let value = &mut *(value as *mut T);
self.state.set(AnimatedBindingState::Animating);
let mut animation_data = self.animation_data.borrow_mut();
animation_data.from_value = value.clone();
self.original_binding
.update((&mut animation_data.to_value) as *mut T as *mut (), context);
self.original_binding.update((&mut animation_data.to_value) as *mut T as *mut ());
let (val, finished) = animation_data.compute_interpolated_value();
*value = val;
if finished {
@ -682,8 +623,8 @@ impl<T: InterpolatedPropertyValue> BindingCallable for AnimatedBindingCallable<T
fn properties_simple_test() {
use std::rc::Rc;
use weak_pin::rc::WeakPin;
fn g(prop: &Property<i32>, ctx: &EvaluationContext) -> i32 {
unsafe { Pin::new_unchecked(prop).get(ctx) }
fn g(prop: &Property<i32>) -> i32 {
unsafe { Pin::new_unchecked(prop).get() }
}
#[derive(Default)]
@ -692,32 +633,27 @@ fn properties_simple_test() {
height: Property<i32>,
area: Property<i32>,
}
let dummy_eval_context = EvaluationContext::for_root_component(unsafe {
core::pin::Pin::new_unchecked(vtable::VRef::from_raw(
core::ptr::NonNull::dangling(),
core::ptr::NonNull::dangling(),
))
});
let compo = Rc::pin(Component::default());
let w = WeakPin::downgrade(compo.clone());
compo.area.set_binding(move |ctx| {
compo.area.set_binding(move || {
let compo = w.upgrade().unwrap();
g(&compo.width, ctx) * g(&compo.height, ctx)
g(&compo.width) * g(&compo.height)
});
compo.width.set(4);
compo.height.set(8);
assert_eq!(g(&compo.width, &dummy_eval_context), 4);
assert_eq!(g(&compo.height, &dummy_eval_context), 8);
assert_eq!(g(&compo.area, &dummy_eval_context), 4 * 8);
assert_eq!(g(&compo.width), 4);
assert_eq!(g(&compo.height), 8);
assert_eq!(g(&compo.area), 4 * 8);
let w = WeakPin::downgrade(compo.clone());
compo.width.set_binding(move |ctx| {
compo.width.set_binding(move || {
let compo = w.upgrade().unwrap();
g(&compo.height, ctx) * 2
g(&compo.height) * 2
});
assert_eq!(g(&compo.width, &dummy_eval_context), 8 * 2);
assert_eq!(g(&compo.height, &dummy_eval_context), 8);
assert_eq!(g(&compo.area, &dummy_eval_context), 8 * 8 * 2);
assert_eq!(g(&compo.width), 8 * 2);
assert_eq!(g(&compo.height), 8);
assert_eq!(g(&compo.area), 8 * 8 * 2);
}
#[allow(non_camel_case_types)]
@ -735,12 +671,8 @@ pub unsafe extern "C" fn sixtyfps_property_init(out: *mut PropertyHandleOpaque)
/// To be called before accessing the value
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_property_update(
handle: &PropertyHandleOpaque,
context: &EvaluationContext,
val: *mut c_void,
) {
handle.0.update(val, context);
pub unsafe extern "C" fn sixtyfps_property_update(handle: &PropertyHandleOpaque, val: *mut c_void) {
handle.0.update(val);
handle.0.register_as_dependency_to_current_binding();
}
@ -753,12 +685,12 @@ pub unsafe extern "C" fn sixtyfps_property_set_changed(handle: &PropertyHandleOp
}
fn make_c_function_binding(
binding: extern "C" fn(*mut c_void, &EvaluationContext, *mut c_void),
binding: extern "C" fn(*mut c_void, *mut c_void),
user_data: *mut c_void,
drop_user_data: Option<extern "C" fn(*mut c_void)>,
) -> impl Fn(*mut (), &EvaluationContext) -> BindingResult {
) -> impl Fn(*mut ()) -> BindingResult {
struct CFunctionBinding<T> {
binding_function: extern "C" fn(*mut c_void, &EvaluationContext, *mut T),
binding_function: extern "C" fn(*mut c_void, *mut T),
user_data: *mut c_void,
drop_user_data: Option<extern "C" fn(*mut c_void)>,
}
@ -773,14 +705,13 @@ fn make_c_function_binding(
let b = CFunctionBinding { binding_function: binding, user_data, drop_user_data };
move |value_ptr, context| {
(b.binding_function)(b.user_data, context, value_ptr);
move |value_ptr| {
(b.binding_function)(b.user_data, value_ptr);
BindingResult::KeepBinding
}
}
/// Set a binding
/// The binding has signature fn(user_data, context, pointer_to_value)
///
/// The current implementation will do usually two memory alocation:
/// 1. the allocation from the calling code to allocate user_data
@ -790,11 +721,7 @@ fn make_c_function_binding(
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_property_set_binding(
handle: &PropertyHandleOpaque,
binding: extern "C" fn(
user_data: *mut c_void,
context: &EvaluationContext,
pointer_to_value: *mut c_void,
),
binding: extern "C" fn(user_data: *mut c_void, pointer_to_value: *mut c_void),
user_data: *mut c_void,
drop_user_data: Option<extern "C" fn(*mut c_void)>,
) {
@ -845,7 +772,7 @@ fn c_set_animated_value<T: InterpolatedPropertyValue>(
animation_data: &crate::abi::primitives::PropertyAnimation,
) {
let d = PropertyValueAnimationData::new(from, to, animation_data.clone());
handle.0.set_binding(move |val: *mut (), _: &EvaluationContext| {
handle.0.set_binding(move |val: *mut ()| {
let (value, finished) = d.compute_interpolated_value();
unsafe {
*(val as *mut T) = value;
@ -895,14 +822,14 @@ pub unsafe extern "C" fn sixtyfps_property_set_animated_value_color(
unsafe fn c_set_animated_binding<T: InterpolatedPropertyValue>(
handle: &PropertyHandleOpaque,
binding: extern "C" fn(*mut c_void, &EvaluationContext, *mut T),
binding: extern "C" fn(*mut c_void, *mut T),
user_data: *mut c_void,
drop_user_data: Option<extern "C" fn(*mut c_void)>,
animation_data: &crate::abi::primitives::PropertyAnimation,
) {
let binding = core::mem::transmute::<
extern "C" fn(*mut c_void, &EvaluationContext, *mut T),
extern "C" fn(*mut c_void, &EvaluationContext, *mut ()),
extern "C" fn(*mut c_void, *mut T),
extern "C" fn(*mut c_void, *mut ()),
>(binding);
handle.0.set_binding(AnimatedBindingCallable::<T> {
original_binding: PropertyHandle {
@ -926,7 +853,7 @@ unsafe fn c_set_animated_binding<T: InterpolatedPropertyValue>(
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_property_set_animated_binding_int(
handle: &PropertyHandleOpaque,
binding: extern "C" fn(*mut c_void, &EvaluationContext, *mut i32),
binding: extern "C" fn(*mut c_void, *mut i32),
user_data: *mut c_void,
drop_user_data: Option<extern "C" fn(*mut c_void)>,
animation_data: &crate::abi::primitives::PropertyAnimation,
@ -938,7 +865,7 @@ pub unsafe extern "C" fn sixtyfps_property_set_animated_binding_int(
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_property_set_animated_binding_float(
handle: &PropertyHandleOpaque,
binding: extern "C" fn(*mut c_void, &EvaluationContext, *mut f32),
binding: extern "C" fn(*mut c_void, *mut f32),
user_data: *mut c_void,
drop_user_data: Option<extern "C" fn(*mut c_void)>,
animation_data: &crate::abi::primitives::PropertyAnimation,
@ -950,7 +877,7 @@ pub unsafe extern "C" fn sixtyfps_property_set_animated_binding_float(
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_property_set_animated_binding_color(
handle: &PropertyHandleOpaque,
binding: extern "C" fn(*mut c_void, &EvaluationContext, *mut Color),
binding: extern "C" fn(*mut c_void, *mut Color),
user_data: *mut c_void,
drop_user_data: Option<extern "C" fn(*mut c_void)>,
animation_data: &crate::abi::primitives::PropertyAnimation,
@ -975,52 +902,44 @@ mod animation_tests {
#[test]
fn properties_test_animation_triggered_by_set() {
fn g(prop: &Property<i32>, ctx: &EvaluationContext) -> i32 {
unsafe { Pin::new_unchecked(prop).get(ctx) }
fn g(prop: &Property<i32>) -> i32 {
unsafe { Pin::new_unchecked(prop).get() }
}
let dummy_eval_context = EvaluationContext {
component: unsafe {
core::pin::Pin::new_unchecked(vtable::VRef::from_raw(
core::ptr::NonNull::dangling(),
core::ptr::NonNull::dangling(),
))
},
parent_context: None,
};
let compo = Rc::new(Component::default());
let w = Rc::downgrade(&compo);
compo.width_times_two.set_binding(move |context| {
compo.width_times_two.set_binding(move || {
let compo = w.upgrade().unwrap();
g(&compo.width, context) * 2
g(&compo.width) * 2
});
let animation_details = PropertyAnimation { duration: DURATION.as_millis() as _ };
compo.width.set(100);
assert_eq!(g(&compo.width, &dummy_eval_context), 100);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 200);
assert_eq!(g(&compo.width), 100);
assert_eq!(g(&compo.width_times_two), 200);
let start_time =
crate::animations::CURRENT_ANIMATION_DRIVER.with(|driver| driver.current_tick());
compo.width.set_animated_value(200, &animation_details);
assert_eq!(g(&compo.width, &dummy_eval_context), 100);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 200);
assert_eq!(g(&compo.width), 100);
assert_eq!(g(&compo.width_times_two), 200);
crate::animations::CURRENT_ANIMATION_DRIVER
.with(|driver| driver.update_animations(start_time + DURATION / 2));
assert_eq!(g(&compo.width, &dummy_eval_context), 150);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 300);
assert_eq!(g(&compo.width), 150);
assert_eq!(g(&compo.width_times_two), 300);
crate::animations::CURRENT_ANIMATION_DRIVER
.with(|driver| driver.update_animations(start_time + DURATION));
assert_eq!(g(&compo.width, &dummy_eval_context), 200);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 400);
assert_eq!(g(&compo.width), 200);
assert_eq!(g(&compo.width_times_two), 400);
crate::animations::CURRENT_ANIMATION_DRIVER
.with(|driver| driver.update_animations(start_time + DURATION * 2));
assert_eq!(g(&compo.width, &dummy_eval_context), 200);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 400);
assert_eq!(g(&compo.width), 200);
assert_eq!(g(&compo.width_times_two), 400);
// the binding should be removed
compo.width.handle.access(|binding| assert!(binding.is_none()));
@ -1028,24 +947,15 @@ mod animation_tests {
#[test]
fn properties_test_animation_triggered_by_binding() {
fn g(prop: &Property<i32>, ctx: &EvaluationContext) -> i32 {
unsafe { Pin::new_unchecked(prop).get(ctx) }
fn g(prop: &Property<i32>) -> i32 {
unsafe { Pin::new_unchecked(prop).get() }
}
let dummy_eval_context = EvaluationContext {
component: unsafe {
core::pin::Pin::new_unchecked(vtable::VRef::from_raw(
core::ptr::NonNull::dangling(),
core::ptr::NonNull::dangling(),
))
},
parent_context: None,
};
let compo = Rc::new(Component::default());
let w = Rc::downgrade(&compo);
compo.width_times_two.set_binding(move |context| {
compo.width_times_two.set_binding(move || {
let compo = w.upgrade().unwrap();
g(&compo.width, context) * 2
g(&compo.width) * 2
});
let start_time =
@ -1055,32 +965,32 @@ mod animation_tests {
let w = Rc::downgrade(&compo);
compo.width.set_animated_binding(
move |context| {
move || {
let compo = w.upgrade().unwrap();
g(&compo.feed_property, context)
g(&compo.feed_property)
},
&animation_details,
);
compo.feed_property.set(100);
assert_eq!(g(&compo.width, &dummy_eval_context), 100);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 200);
assert_eq!(g(&compo.width), 100);
assert_eq!(g(&compo.width_times_two), 200);
compo.feed_property.set(200);
assert_eq!(g(&compo.width, &dummy_eval_context), 100);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 200);
assert_eq!(g(&compo.width), 100);
assert_eq!(g(&compo.width_times_two), 200);
crate::animations::CURRENT_ANIMATION_DRIVER
.with(|driver| driver.update_animations(start_time + DURATION / 2));
assert_eq!(g(&compo.width, &dummy_eval_context), 150);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 300);
assert_eq!(g(&compo.width), 150);
assert_eq!(g(&compo.width_times_two), 300);
crate::animations::CURRENT_ANIMATION_DRIVER
.with(|driver| driver.update_animations(start_time + DURATION));
assert_eq!(g(&compo.width, &dummy_eval_context), 200);
assert_eq!(g(&compo.width_times_two, &dummy_eval_context), 400);
assert_eq!(g(&compo.width), 200);
assert_eq!(g(&compo.width_times_two), 400);
}
}
@ -1094,7 +1004,7 @@ impl Default for PropertyListenerScope {
fn default() -> Self {
static VT: &'static BindingVTable = &BindingVTable {
drop: |_| (),
evaluate: |_, _, _| BindingResult::KeepBinding,
evaluate: |_, _| BindingResult::KeepBinding,
mark_dirty: |_| (),
};
@ -1134,21 +1044,13 @@ fn test_property_listener_scope() {
let scope = Box::pin(PropertyListenerScope::default());
let prop1 = Box::pin(Property::new(42));
assert!(scope.is_dirty()); // It is dirty at the beginning
let dummy_eval_context = EvaluationContext {
component: unsafe {
core::pin::Pin::new_unchecked(vtable::VRef::from_raw(
core::ptr::NonNull::dangling(),
core::ptr::NonNull::dangling(),
))
},
parent_context: None,
};
let r = scope.as_ref().evaluate(|| prop1.as_ref().get(&dummy_eval_context));
let r = scope.as_ref().evaluate(|| prop1.as_ref().get());
assert_eq!(r, 42);
assert!(!scope.is_dirty()); // It is no longer dirty
prop1.as_ref().set(88);
assert!(scope.is_dirty()); // now dirty for prop1 changed.
let r = scope.as_ref().evaluate(|| prop1.as_ref().get(&dummy_eval_context) + 1);
let r = scope.as_ref().evaluate(|| prop1.as_ref().get() + 1);
assert_eq!(r, 89);
assert!(!scope.is_dirty());
let r = scope.as_ref().evaluate(|| 12);

View file

@ -5,7 +5,6 @@ TODO: reconsider if we should rename that to `Event`
but then it should also be renamed everywhere, including in the language grammar
*/
use super::properties::EvaluationContext;
use core::cell::Cell;
/// A Signal that can be connected to a handler.
@ -16,16 +15,14 @@ use core::cell::Cell;
#[repr(C)]
pub struct Signal<Arg> {
/// FIXME: Box<dyn> is a fat object and we probaly want to put an erased type in there
handler: Cell<Option<Box<dyn Fn(&EvaluationContext, Arg)>>>,
handler: Cell<Option<Box<dyn Fn(Arg)>>>,
}
impl<Arg> Signal<Arg> {
/// Emit the signal with the given argument.
///
/// The constext must be a context corresponding to the component in which the signal is contained.
pub fn emit(&self, context: &EvaluationContext, a: Arg) {
pub fn emit(&self, a: Arg) {
if let Some(h) = self.handler.take() {
h(context, a);
h(a);
assert!(self.handler.take().is_none(), "Signal Handler set while emitted");
self.handler.set(Some(h))
}
@ -34,44 +31,23 @@ impl<Arg> Signal<Arg> {
/// Set an handler to be called when the signal is emited
///
/// There can only be one single handler per signal.
pub fn set_handler(&self, f: impl Fn(&EvaluationContext, Arg) + 'static) {
pub fn set_handler(&self, f: impl Fn(Arg) + 'static) {
self.handler.set(Some(Box::new(f)));
}
}
#[test]
fn signal_simple_test() {
use std::pin::Pin;
use std::rc::Rc;
#[derive(Default)]
struct Component {
pressed: core::cell::Cell<bool>,
clicked: Signal<()>,
}
impl crate::abi::datastructures::Component for Component {
fn visit_children_item(
self: Pin<&Self>,
_: isize,
_: crate::abi::datastructures::ItemVisitorRefMut,
) {
}
fn layout_info(self: Pin<&Self>) -> crate::abi::datastructures::LayoutInfo {
unimplemented!()
}
fn compute_layout(self: Pin<&Self>, _: &crate::EvaluationContext) {}
}
use crate::abi::datastructures::ComponentVTable;
let c = Component::default();
c.clicked.set_handler(|c, ()| unsafe {
(*(c.component.as_ptr() as *const Component)).pressed.set(true)
});
let vtable = ComponentVTable::new::<Component>();
let ctx = super::properties::EvaluationContext::for_root_component(unsafe {
Pin::new_unchecked(vtable::VRef::from_raw(
core::ptr::NonNull::from(&vtable),
core::ptr::NonNull::from(&c).cast(),
))
});
c.clicked.emit(&ctx, ());
let c = Rc::new(Component::default());
let weak = Rc::downgrade(&c);
c.clicked.set_handler(move |()| weak.upgrade().unwrap().pressed.set(true));
c.clicked.emit(());
assert_eq!(c.pressed.get(), true);
}
@ -94,21 +70,18 @@ pub unsafe extern "C" fn sixtyfps_signal_init(out: *mut SignalOpaque) {
/// Emit the signal
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_signal_emit(
sig: *const SignalOpaque,
component: &EvaluationContext,
) {
pub unsafe extern "C" fn sixtyfps_signal_emit(sig: *const SignalOpaque) {
let sig = &*(sig as *const Signal<()>);
sig.emit(component, ());
sig.emit(());
}
/// Set signal handler.
///
/// The binding has signature fn(user_data, context)
/// The binding has signature fn(user_data)
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_signal_set_handler(
sig: *mut SignalOpaque,
binding: extern "C" fn(*mut c_void, &EvaluationContext),
binding: extern "C" fn(user_data: *mut c_void),
user_data: *mut c_void,
drop_user_data: Option<extern "C" fn(*mut c_void)>,
) {
@ -128,8 +101,8 @@ pub unsafe extern "C" fn sixtyfps_signal_set_handler(
}
let ud = UserData { user_data, drop_user_data };
let real_binding = move |compo: &EvaluationContext, ()| {
binding(ud.user_data, compo);
let real_binding = move |()| {
binding(ud.user_data);
};
sig.set_handler(real_binding);
}

View file

@ -39,15 +39,7 @@ impl AnimationDriver {
/// The current instant that is to be used for animation
/// using this function register the current binding as a dependency
pub fn current_tick(&self) -> instant::Instant {
// FIXME! we need to get rid of the contect there
#[allow(unsafe_code)]
let dummy_eval_context = crate::EvaluationContext::for_root_component(unsafe {
core::pin::Pin::new_unchecked(vtable::VRef::from_raw(
core::ptr::NonNull::dangling(),
core::ptr::NonNull::dangling(),
))
});
self.global_instant.as_ref().get(&dummy_eval_context)
self.global_instant.as_ref().get()
}
}

View file

@ -145,7 +145,7 @@ impl<Backend: GraphicsBackend> crate::eventloop::GenericWindow
{
fn draw(&self, component: crate::ComponentRefPin) {
// FIXME: we should do that only if some property change
component.as_ref().compute_layout(&crate::EvaluationContext::for_root_component(component));
component.as_ref().compute_layout();
let mut this = self.borrow_mut();
@ -156,9 +156,8 @@ impl<Backend: GraphicsBackend> crate::eventloop::GenericWindow
// Generate cached rendering data once
crate::item_tree::visit_items(
component,
|ctx, item, _| {
|_, item, _| {
crate::item_rendering::update_item_rendering_data(
ctx,
item,
&mut this.rendering_cache,
&mut rendering_primitives_builder,

View file

@ -12,14 +12,14 @@ pub fn process_mouse_event(component: ComponentRefPin, event: MouseEvent) {
crate::item_tree::visit_items(
component,
|context, item, offset| {
let geom = item.as_ref().geometry(context);
|_, item, offset| {
let geom = item.as_ref().geometry();
let geom = geom.translate(*offset);
if geom.contains(event.pos) {
let mut event2 = event.clone();
event2.pos -= geom.origin.to_vector();
item.as_ref().input_event(event2, context);
item.as_ref().input_event(event2);
}
geom.origin.to_vector()

View file

@ -2,16 +2,14 @@ use super::abi::datastructures::ItemRef;
use super::graphics::{
Frame, GraphicsBackend, HasRenderingPrimitive, RenderingCache, RenderingPrimitivesBuilder,
};
use super::EvaluationContext;
use cgmath::{Matrix4, SquareMatrix, Vector3};
pub(crate) fn update_item_rendering_data<Backend: GraphicsBackend>(
context: &EvaluationContext,
item: core::pin::Pin<ItemRef>,
rendering_cache: &mut RenderingCache<Backend>,
rendering_primitives_builder: &mut Backend::RenderingPrimitivesBuilder,
) {
let item_rendering_primitive = item.as_ref().rendering_primitive(context);
let item_rendering_primitive = item.as_ref().rendering_primitive();
let rendering_data = item.cached_rendering_data_offset();
@ -41,8 +39,8 @@ pub(crate) fn render_component_items<Backend: GraphicsBackend>(
crate::item_tree::visit_items(
component,
|context, item, transform| {
let origin = item.as_ref().geometry(context).origin;
|_, item, transform| {
let origin = item.as_ref().geometry().origin;
let transform =
transform * Matrix4::from_translation(Vector3::new(origin.x, origin.y, 0.));

View file

@ -1,6 +1,5 @@
use crate::abi::datastructures::{ItemRef, ItemTreeNode, ItemVisitor, ItemVisitorVTable};
use crate::ComponentRefPin;
use crate::EvaluationContext;
use core::pin::Pin;
/// Visit each items recursively
@ -8,31 +7,24 @@ use core::pin::Pin;
/// The state parametter returned by the visitor is passed to each children.
pub fn visit_items<State>(
component: ComponentRefPin,
mut visitor: impl FnMut(&EvaluationContext, Pin<ItemRef>, &State) -> State,
mut visitor: impl FnMut(ComponentRefPin, Pin<ItemRef>, &State) -> State,
state: State,
) {
let context = EvaluationContext::for_root_component(component);
visit_internal(&context, &mut visitor, -1, &state)
visit_internal(component, &mut visitor, -1, &state)
}
fn visit_internal<State>(
context: &EvaluationContext,
visitor: &mut impl FnMut(&EvaluationContext, Pin<ItemRef>, &State) -> State,
component: ComponentRefPin,
visitor: &mut impl FnMut(ComponentRefPin, Pin<ItemRef>, &State) -> State,
index: isize,
state: &State,
) {
let mut actual_visitor = |component: ComponentRefPin, index: isize, item: Pin<ItemRef>| {
if component.as_ptr() == context.component.as_ptr() {
let s = visitor(context, item, state);
visit_internal(context, visitor, index, &s);
} else {
let context = context.child_context(component);
let s = visitor(&context, item, state);
visit_internal(&context, visitor, index, &s);
}
let s = visitor(component, item, state);
visit_internal(component, visitor, index, &s);
};
vtable::new_vref!(let mut actual_visitor : VRefMut<ItemVisitorVTable> for ItemVisitor = &mut actual_visitor);
context.component.as_ref().visit_children_item(index, actual_visitor);
component.as_ref().visit_children_item(index, actual_visitor);
}
/// Visit the children within an array of ItemTreeNode

View file

@ -44,7 +44,7 @@ pub use abi::sharedarray::SharedArray;
pub use abi::datastructures::Resource;
#[doc(inline)]
pub use abi::properties::{EvaluationContext, Property};
pub use abi::properties::Property;
#[doc(inline)]
pub use abi::signals::Signal;

View file

@ -27,7 +27,7 @@ declare_ValueType![
];
pub trait PropertyInfo<Item, Value> {
fn get(&self, item: Pin<&Item>, context: &crate::EvaluationContext) -> Result<Value, ()>;
fn get(&self, item: Pin<&Item>) -> Result<Value, ()>;
fn set(
&self,
item: Pin<&Item>,
@ -37,7 +37,7 @@ pub trait PropertyInfo<Item, Value> {
fn set_binding(
&self,
item: Pin<&Item>,
binding: Box<dyn Fn(&crate::EvaluationContext) -> Value>,
binding: Box<dyn Fn() -> Value>,
animation: Option<crate::abi::primitives::PropertyAnimation>,
) -> Result<(), ()>;
@ -61,8 +61,8 @@ where
Value: TryInto<T>,
T: TryInto<Value>,
{
fn get(&self, item: Pin<&Item>, context: &crate::EvaluationContext) -> Result<Value, ()> {
self.apply_pin(item).get(context).try_into().map_err(|_| ())
fn get(&self, item: Pin<&Item>) -> Result<Value, ()> {
self.apply_pin(item).get().try_into().map_err(|_| ())
}
fn set(
&self,
@ -80,14 +80,14 @@ where
fn set_binding(
&self,
item: Pin<&Item>,
binding: Box<dyn Fn(&crate::EvaluationContext) -> Value>,
binding: Box<dyn Fn() -> Value>,
animation: Option<crate::abi::primitives::PropertyAnimation>,
) -> Result<(), ()> {
if animation.is_some() {
Err(())
} else {
self.apply_pin(item).set_binding(move |context| {
binding(context).try_into().map_err(|_| ()).expect("binding was of the wrong type")
self.apply_pin(item).set_binding(move || {
binding().try_into().map_err(|_| ()).expect("binding was of the wrong type")
});
Ok(())
}
@ -109,8 +109,8 @@ where
T: TryInto<Value>,
T: crate::abi::properties::InterpolatedPropertyValue,
{
fn get(&self, item: Pin<&Item>, context: &crate::EvaluationContext) -> Result<Value, ()> {
self.0.get(item, context)
fn get(&self, item: Pin<&Item>) -> Result<Value, ()> {
self.0.get(item)
}
fn set(
&self,
@ -128,16 +128,13 @@ where
fn set_binding(
&self,
item: Pin<&Item>,
binding: Box<dyn Fn(&crate::EvaluationContext) -> Value>,
binding: Box<dyn Fn() -> Value>,
animation: Option<crate::abi::primitives::PropertyAnimation>,
) -> Result<(), ()> {
if let Some(animation) = &animation {
self.apply_pin(item).set_animated_binding(
move |context| {
binding(context)
.try_into()
.map_err(|_| ())
.expect("binding was of the wrong type")
move || {
binding().try_into().map_err(|_| ()).expect("binding was of the wrong type")
},
animation,
);

View file

@ -13,7 +13,7 @@ use sixtyfps_corelib::abi::primitives::PropertyAnimation;
use sixtyfps_corelib::abi::{properties::PropertyListenerScope, slice::Slice};
use sixtyfps_corelib::rtti::PropertyInfo;
use sixtyfps_corelib::ComponentRefPin;
use sixtyfps_corelib::{rtti, Color, EvaluationContext, Property, SharedString, Signal};
use sixtyfps_corelib::{rtti, Color, Property, SharedString, Signal};
use std::collections::HashMap;
use std::{pin::Pin, rc::Rc};
@ -129,28 +129,24 @@ unsafe extern "C" fn visit_children_item(
&*(component.as_ptr().add(listener_offset) as *const PropertyListenerScope),
);
if listener.is_dirty() {
let eval_context = EvaluationContext::for_root_component(component);
listener.evaluate(|| {
match eval::eval_expression(
&rep_in_comp.model,
&*component_type,
&eval_context,
) {
match eval::eval_expression(&rep_in_comp.model, &*component_type, component)
{
crate::Value::Number(count) => populate_model(
vec,
rep_in_comp,
&eval_context,
component,
(0..count as i32)
.into_iter()
.map(|v| crate::Value::Number(v as f64)),
),
crate::Value::Array(a) => {
populate_model(vec, rep_in_comp, &eval_context, a.into_iter())
populate_model(vec, rep_in_comp, component, a.into_iter())
}
crate::Value::Bool(b) => populate_model(
vec,
rep_in_comp,
&eval_context,
component,
(if b { Some(crate::Value::Void) } else { None }).into_iter(),
),
_ => panic!("Unsupported model"),
@ -370,7 +366,7 @@ fn generate_component(
fn animation_for_property(
component_type: Rc<ComponentDescription>,
eval_context: &EvaluationContext,
eval_context: ComponentRefPin,
all_animations: &HashMap<String, ElementRc>,
property_name: &String,
) -> Option<PropertyAnimation> {
@ -386,7 +382,7 @@ fn animation_for_property(
fn animation_for_element_property(
component_type: Rc<ComponentDescription>,
eval_context: &EvaluationContext,
eval_context: ComponentRefPin,
element: &Element,
property_name: &String,
) -> Option<PropertyAnimation> {
@ -401,11 +397,11 @@ fn animation_for_element_property(
fn populate_model(
vec: &mut Vec<ComponentBox>,
rep_in_comp: &RepeaterWithinComponent,
eval_context: &EvaluationContext,
component: ComponentRefPin,
model: impl Iterator<Item = eval::Value> + ExactSizeIterator,
) {
vec.resize_with(model.size_hint().1.unwrap(), || {
instantiate(rep_in_comp.component_to_repeat.clone(), Some(eval_context))
instantiate(rep_in_comp.component_to_repeat.clone(), Some(component))
});
for (i, (x, val)) in vec.iter().zip(model).enumerate() {
rep_in_comp
@ -418,20 +414,17 @@ fn populate_model(
pub fn instantiate(
component_type: Rc<ComponentDescription>,
parent_ctx: Option<&EvaluationContext>,
parent_ctx: Option<ComponentRefPin>,
) -> ComponentBox {
let instance = component_type.dynamic_type.clone().create_instance();
let mem = instance.as_ptr().as_ptr() as *mut u8;
let component_box = ComponentBox { instance, component_type: component_type.clone() };
let eval_context = if let Some(parent) = parent_ctx {
if let Some(parent) = parent_ctx {
unsafe {
*(mem.add(component_type.parent_component_offset.unwrap())
as *mut Option<ComponentRefPin>) = Some(parent.component);
as *mut Option<ComponentRefPin>) = Some(parent);
}
parent.child_context(component_box.borrow())
} else {
EvaluationContext::for_root_component(component_box.borrow())
};
for item_within_component in component_type.items.values() {
@ -453,8 +446,13 @@ pub fn instantiate(
as *mut Signal<()>);
let expr = expr.clone();
let component_type = component_type.clone();
signal.set_handler(move |eval_context, _| {
eval::eval_expression(&expr, &*component_type, &eval_context);
let instance = component_box.instance.as_ptr();
signal.set_handler(move |_| {
let c = Pin::new_unchecked(vtable::VRef::from_raw(
NonNull::from(&component_type.ct).cast(),
instance.cast(),
));
eval::eval_expression(&expr, &*component_type, c);
})
} else {
if let Some(prop_rtti) =
@ -462,7 +460,7 @@ pub fn instantiate(
{
let maybe_animation = animation_for_element_property(
component_type.clone(),
&eval_context,
component_box.borrow(),
&elem,
prop,
);
@ -470,17 +468,26 @@ pub fn instantiate(
if expr.is_constant() {
prop_rtti.set(
item,
eval::eval_expression(expr, &*component_type, &eval_context),
eval::eval_expression(
expr,
&*component_type,
component_box.borrow(),
),
maybe_animation,
);
} else {
let expr = expr.clone();
let component_type = component_type.clone();
let instance = component_box.instance.as_ptr();
prop_rtti.set_binding(
item,
Box::new(move |eval_context| {
eval::eval_expression(&expr, &*component_type, eval_context)
Box::new(move || {
let c = Pin::new_unchecked(vtable::VRef::from_raw(
NonNull::from(&component_type.ct).cast(),
instance.cast(),
));
eval::eval_expression(&expr, &*component_type, c)
}),
maybe_animation,
);
@ -491,24 +498,33 @@ pub fn instantiate(
{
let maybe_animation = animation_for_property(
component_type.clone(),
&eval_context,
component_box.borrow(),
&component_type.original.root_element.borrow().property_animations,
prop,
);
if expr.is_constant() {
let v = eval::eval_expression(expr, &*component_type, &eval_context);
let v = eval::eval_expression(
expr,
&*component_type,
component_box.borrow(),
);
prop_info
.set(Pin::new_unchecked(&*mem.add(*offset)), v, maybe_animation)
.unwrap();
} else {
let expr = expr.clone();
let component_type = component_type.clone();
let instance = component_box.instance.as_ptr();
prop_info
.set_binding(
Pin::new_unchecked(&*mem.add(*offset)),
Box::new(move |eval_context| {
eval::eval_expression(&expr, &*component_type, eval_context)
Box::new(move || {
let c = Pin::new_unchecked(vtable::VRef::from_raw(
NonNull::from(&component_type.ct).cast(),
instance.cast(),
));
eval::eval_expression(&expr, &*component_type, c)
}),
maybe_animation,
)
@ -527,20 +543,20 @@ pub fn instantiate(
continue;
}
let vec = unsafe { &mut *(mem.add(rep_in_comp.offset) as *mut RepeaterVec) };
match eval::eval_expression(&rep_in_comp.model, &*component_type, &eval_context) {
match eval::eval_expression(&rep_in_comp.model, &*component_type, component_box.borrow()) {
crate::Value::Number(count) => populate_model(
vec,
rep_in_comp,
&eval_context,
component_box.borrow(),
(0..count as i32).into_iter().map(|v| crate::Value::Number(v as f64)),
),
crate::Value::Array(a) => {
populate_model(vec, rep_in_comp, &eval_context, a.into_iter())
populate_model(vec, rep_in_comp, component_box.borrow(), a.into_iter())
}
crate::Value::Bool(b) => populate_model(
vec,
rep_in_comp,
&eval_context,
component_box.borrow(),
(if b { Some(crate::Value::Void) } else { None }).into_iter(),
),
_ => panic!("Unsupported model"),
@ -550,17 +566,13 @@ pub fn instantiate(
component_box
}
unsafe extern "C" fn compute_layout(component: ComponentRefPin, eval_context: &EvaluationContext) {
debug_assert!(component.as_ptr() == eval_context.component.as_ptr());
unsafe extern "C" fn compute_layout(component: ComponentRefPin) {
// This is fine since we can only be called with a component that with our vtable which is a ComponentDescription
let component_type =
&*(component.get_vtable() as *const ComponentVTable as *const ComponentDescription);
let resolve_prop_ref = |prop_ref: &expression_tree::Expression| {
eval::eval_expression(&prop_ref, &component_type, eval_context)
.try_into()
.unwrap_or_default()
eval::eval_expression(&prop_ref, &component_type, component).try_into().unwrap_or_default()
};
for it in &component_type.original.layout_constraints.borrow().grids {
@ -605,7 +617,7 @@ unsafe extern "C" fn compute_layout(component: ComponentRefPin, eval_context: &E
let within_info = &component_type.items[it.within.borrow().id.as_str()];
let within_prop = |name| {
within_info.rtti.properties[name]
.get(within_info.item_from_component(component.as_ptr()), &eval_context)
.get(within_info.item_from_component(component.as_ptr()))
.try_into()
.unwrap()
};
@ -639,7 +651,7 @@ unsafe extern "C" fn compute_layout(component: ComponentRefPin, eval_context: &E
})
.collect::<Vec<_>>();
let path_elements = eval::convert_path(&it.path, component_type, eval_context);
let path_elements = eval::convert_path(&it.path, component_type, component);
solve_path_layout(&PathLayoutData {
items: Slice::from(items.as_slice()),

View file

@ -7,18 +7,18 @@ use sixtyfps_compilerlib::{object_tree::ElementRc, typeregister::Type};
use sixtyfps_corelib as corelib;
use sixtyfps_corelib::{
abi::datastructures::ItemRef, abi::datastructures::PathElement,
abi::primitives::PropertyAnimation, Color, EvaluationContext, PathData, Resource, SharedArray,
abi::primitives::PropertyAnimation, Color, ComponentRefPin, PathData, Resource, SharedArray,
SharedString,
};
use std::{collections::HashMap, rc::Rc};
pub trait ErasedPropertyInfo {
fn get(&self, item: Pin<ItemRef>, context: &EvaluationContext) -> Value;
fn get(&self, item: Pin<ItemRef>) -> Value;
fn set(&self, item: Pin<ItemRef>, value: Value, animation: Option<PropertyAnimation>);
fn set_binding(
&self,
item: Pin<ItemRef>,
binding: Box<dyn Fn(&EvaluationContext) -> Value>,
binding: Box<dyn Fn() -> Value>,
animation: Option<PropertyAnimation>,
);
fn offset(&self) -> usize;
@ -27,8 +27,8 @@ pub trait ErasedPropertyInfo {
impl<Item: vtable::HasStaticVTable<corelib::abi::datastructures::ItemVTable>> ErasedPropertyInfo
for &'static dyn corelib::rtti::PropertyInfo<Item, Value>
{
fn get(&self, item: Pin<ItemRef>, context: &EvaluationContext) -> Value {
(*self).get(ItemRef::downcast_pin(item).unwrap(), context).unwrap()
fn get(&self, item: Pin<ItemRef>) -> Value {
(*self).get(ItemRef::downcast_pin(item).unwrap()).unwrap()
}
fn set(&self, item: Pin<ItemRef>, value: Value, animation: Option<PropertyAnimation>) {
(*self).set(ItemRef::downcast_pin(item).unwrap(), value, animation).unwrap()
@ -36,7 +36,7 @@ impl<Item: vtable::HasStaticVTable<corelib::abi::datastructures::ItemVTable>> Er
fn set_binding(
&self,
item: Pin<ItemRef>,
binding: Box<dyn Fn(&EvaluationContext) -> Value>,
binding: Box<dyn Fn() -> Value>,
animation: Option<PropertyAnimation>,
) {
(*self).set_binding(ItemRef::downcast_pin(item).unwrap(), binding, animation).unwrap();
@ -119,7 +119,7 @@ declare_value_conversion!(PathElements => [PathData]);
pub fn eval_expression(
e: &Expression,
component_type: &crate::ComponentDescription,
eval_context: &corelib::EvaluationContext,
component_ref: ComponentRefPin,
) -> Value {
match e {
Expression::Invalid => panic!("invalid expression while evaluating"),
@ -130,23 +130,21 @@ pub fn eval_expression(
Expression::SignalReference { .. } => panic!("signal in expression"),
Expression::PropertyReference(NamedReference { element, name }) => {
let element = element.upgrade().unwrap();
let (component_mem, component_type, eval_context) =
enclosing_component_for_element(&element, component_type, eval_context);
let (component_mem, component_type, _) =
enclosing_component_for_element(&element, component_type, component_ref);
let element = element.borrow();
if element.id == element.enclosing_component.upgrade().unwrap().root_element.borrow().id
{
if let Some(x) = component_type.custom_properties.get(name) {
return unsafe {
x.prop
.get(Pin::new_unchecked(&*component_mem.add(x.offset)), &eval_context)
.unwrap()
x.prop.get(Pin::new_unchecked(&*component_mem.add(x.offset))).unwrap()
};
}
};
let item_info = &component_type.items[element.id.as_str()];
core::mem::drop(element);
let item = unsafe { item_info.item_from_component(component_mem) };
item_info.rtti.properties[name.as_str()].get(item, &eval_context)
item_info.rtti.properties[name.as_str()].get(item)
}
Expression::RepeaterIndexReference { element } => {
if element.upgrade().unwrap().borrow().base_type
@ -154,12 +152,7 @@ pub fn eval_expression(
{
let x = &component_type.custom_properties["index"];
unsafe {
x.prop
.get(
Pin::new_unchecked(&*eval_context.component.as_ptr().add(x.offset)),
&eval_context,
)
.unwrap()
x.prop.get(Pin::new_unchecked(&*component_ref.as_ptr().add(x.offset))).unwrap()
}
} else {
todo!();
@ -171,26 +164,21 @@ pub fn eval_expression(
{
let x = &component_type.custom_properties["model_data"];
unsafe {
x.prop
.get(
Pin::new_unchecked(&*eval_context.component.as_ptr().add(x.offset)),
&eval_context,
)
.unwrap()
x.prop.get(Pin::new_unchecked(&*component_ref.as_ptr().add(x.offset))).unwrap()
}
} else {
todo!();
}
}
Expression::ObjectAccess { base, name } => {
if let Value::Object(mut o) = eval_expression(base, component_type, eval_context) {
if let Value::Object(mut o) = eval_expression(base, component_type, component_ref) {
o.remove(name).unwrap_or(Value::Void)
} else {
Value::Void
}
}
Expression::Cast { from, to } => {
let v = eval_expression(&*from, component_type, eval_context);
let v = eval_expression(&*from, component_type, component_ref);
match (v, to) {
(Value::Number(n), Type::Int32) => Value::Number(n.round()),
(Value::Number(n), Type::String) => {
@ -203,15 +191,15 @@ pub fn eval_expression(
Expression::CodeBlock(sub) => {
let mut v = Value::Void;
for e in sub {
v = eval_expression(e, component_type, eval_context);
v = eval_expression(e, component_type, component_ref);
}
v
}
Expression::FunctionCall { function, .. } => {
if let Expression::SignalReference(NamedReference { element, name }) = &**function {
let element = element.upgrade().unwrap();
let (component_mem, component_type, eval_context) =
enclosing_component_for_element(&element, component_type, eval_context);
let (component_mem, component_type, _) =
enclosing_component_for_element(&element, component_type, component_ref);
let item_info = &component_type.items[element.borrow().id.as_str()];
let item = unsafe { item_info.item_from_component(component_mem) };
@ -230,7 +218,7 @@ pub fn eval_expression(
.unwrap_or_else(|| panic!("unkown signal {}", name))
as *mut corelib::Signal<()>)
};
signal.emit(&eval_context, ());
signal.emit(());
Value::Void
} else {
panic!("call of something not a signal")
@ -239,7 +227,7 @@ pub fn eval_expression(
Expression::SelfAssignment { lhs, rhs, op } => match &**lhs {
Expression::PropertyReference(NamedReference { element, name }) => {
let eval = |lhs| {
let rhs = eval_expression(&**rhs, component_type, eval_context);
let rhs = eval_expression(&**rhs, component_type, component_ref);
match (lhs, rhs, op) {
(Value::Number(a), Value::Number(b), '+') => Value::Number(a + b),
(Value::Number(a), Value::Number(b), '-') => Value::Number(a - b),
@ -250,17 +238,15 @@ pub fn eval_expression(
};
let element = element.upgrade().unwrap();
let (component_mem, component_type, eval_context) =
enclosing_component_for_element(&element, component_type, eval_context);
let (component_mem, component_type, _) =
enclosing_component_for_element(&element, component_type, component_ref);
let component = element.borrow().enclosing_component.upgrade().unwrap();
if element.borrow().id == component.root_element.borrow().id {
if let Some(x) = component_type.custom_properties.get(name) {
unsafe {
let p = Pin::new_unchecked(&*component_mem.add(x.offset));
x.prop
.set(p, eval(x.prop.get(p, &eval_context).unwrap()), None)
.unwrap();
x.prop.set(p, eval(x.prop.get(p).unwrap()), None).unwrap();
}
return Value::Void;
}
@ -268,14 +254,14 @@ pub fn eval_expression(
let item_info = &component_type.items[element.borrow().id.as_str()];
let item = unsafe { item_info.item_from_component(component_mem) };
let p = &item_info.rtti.properties[name.as_str()];
p.set(item, eval(p.get(item, &eval_context)), None);
p.set(item, eval(p.get(item)), None);
Value::Void
}
_ => panic!("typechecking should make sure this was a PropertyReference"),
},
Expression::BinaryExpression { lhs, rhs, op } => {
let lhs = eval_expression(&**lhs, component_type, eval_context);
let rhs = eval_expression(&**rhs, component_type, eval_context);
let lhs = eval_expression(&**lhs, component_type, component_ref);
let rhs = eval_expression(&**rhs, component_type, component_ref);
match (op, lhs, rhs) {
('+', Value::Number(a), Value::Number(b)) => Value::Number(a + b),
@ -294,7 +280,7 @@ pub fn eval_expression(
}
}
Expression::UnaryOp { sub, op } => {
let sub = eval_expression(&**sub, component_type, eval_context);
let sub = eval_expression(&**sub, component_type, component_ref);
match (sub, op) {
(Value::Number(a), '+') => Value::Number(a),
(Value::Number(a), '-') => Value::Number(-a),
@ -306,25 +292,25 @@ pub fn eval_expression(
Value::Resource(Resource::AbsoluteFilePath(absolute_source_path.as_str().into()))
}
Expression::Condition { condition, true_expr, false_expr } => {
match eval_expression(&**condition, component_type, eval_context).try_into()
match eval_expression(&**condition, component_type, component_ref).try_into()
as Result<bool, _>
{
Ok(true) => eval_expression(&**true_expr, component_type, eval_context),
Ok(false) => eval_expression(&**false_expr, component_type, eval_context),
Ok(true) => eval_expression(&**true_expr, component_type, component_ref),
Ok(false) => eval_expression(&**false_expr, component_type, component_ref),
_ => panic!("conditional expression did not evaluate to boolean"),
}
}
Expression::Array { values, .. } => Value::Array(
values.iter().map(|e| eval_expression(e, component_type, eval_context)).collect(),
values.iter().map(|e| eval_expression(e, component_type, component_ref)).collect(),
),
Expression::Object { values, .. } => Value::Object(
values
.iter()
.map(|(k, v)| (k.clone(), eval_expression(v, component_type, eval_context)))
.map(|(k, v)| (k.clone(), eval_expression(v, component_type, component_ref)))
.collect(),
),
Expression::PathElements { elements } => {
Value::PathElements(convert_path(elements, component_type, eval_context))
Value::PathElements(convert_path(elements, component_type, component_ref))
}
}
}
@ -332,16 +318,16 @@ pub fn eval_expression(
fn enclosing_component_for_element<'a>(
element: &ElementRc,
component_type: &'a crate::ComponentDescription,
eval_context: &corelib::EvaluationContext<'a>,
) -> (*const u8, &'a crate::ComponentDescription, corelib::EvaluationContext<'a>) {
component_ref: ComponentRefPin<'a>,
) -> (*const u8, &'a crate::ComponentDescription, ComponentRefPin<'a>) {
if Rc::ptr_eq(
&element.borrow().enclosing_component.upgrade().unwrap(),
&component_type.original,
) {
let mem = eval_context.component.as_ptr();
(mem, component_type, EvaluationContext::for_root_component(eval_context.component))
let mem = component_ref.as_ptr();
(mem, component_type, component_ref)
} else {
let mem = eval_context.component.as_ptr();
let mem = component_ref.as_ptr();
let parent_component = unsafe {
*(mem.add(component_type.parent_component_offset.unwrap())
as *const Option<corelib::ComponentRefPin>)
@ -349,11 +335,7 @@ fn enclosing_component_for_element<'a>(
.unwrap();
let parent_component_type =
unsafe { crate::dynamic_component::get_component_type(parent_component) };
enclosing_component_for_element(
element,
parent_component_type,
&EvaluationContext::for_root_component(parent_component),
)
enclosing_component_for_element(element, parent_component_type, parent_component)
}
}
@ -362,12 +344,12 @@ pub fn new_struct_with_bindings<
>(
bindings: &HashMap<String, Expression>,
component_type: &crate::ComponentDescription,
eval_context: &corelib::EvaluationContext,
component_ref: ComponentRefPin,
) -> ElementType {
let mut element = ElementType::default();
for (prop, info) in ElementType::fields::<Value>().into_iter() {
if let Some(binding) = &bindings.get(prop) {
let value = eval_expression(&binding, &*component_type, &eval_context);
let value = eval_expression(&binding, &*component_type, component_ref);
info.set_field(&mut element, value).unwrap();
}
}
@ -428,7 +410,7 @@ fn convert_from_lyon_path<'a>(
pub fn convert_path(
path: &ExprPath,
component_type: &crate::ComponentDescription,
eval_context: &corelib::EvaluationContext,
eval_context: ComponentRefPin,
) -> PathData {
match path {
ExprPath::Elements(elements) => PathData::Elements(SharedArray::<PathElement>::from_iter(
@ -443,7 +425,7 @@ pub fn convert_path(
fn convert_path_element(
expr_element: &ExprPathElement,
component_type: &crate::ComponentDescription,
eval_context: &corelib::EvaluationContext,
eval_context: ComponentRefPin,
) -> PathElement {
match expr_element.element_type.class_name.as_str() {
"LineTo" => PathElement::LineTo(new_struct_with_bindings(

View file

@ -16,7 +16,7 @@ pub use eval::Value;
pub use dynamic_component::ComponentBox;
use sixtyfps_corelib::abi::datastructures::{ComponentRef, ComponentRefMut};
use sixtyfps_corelib::{ComponentRefPin, EvaluationContext, Signal};
use sixtyfps_corelib::{ComponentRefPin, Signal};
use std::{collections::HashMap, pin::Pin, rc::Rc};
impl ComponentDescription {
@ -66,7 +66,7 @@ impl ComponentDescription {
&self,
component: ComponentRef,
name: &str,
binding: Box<dyn Fn(&EvaluationContext) -> Value>,
binding: Box<dyn Fn() -> Value>,
) -> Result<(), ()> {
if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
return Err(());
@ -84,17 +84,12 @@ impl ComponentDescription {
///
/// Returns an error if the component is not an instance corresponding to this ComponentDescription,
/// or if a signal with this name does not exist in this component
pub fn get_property(&self, eval_context: &EvaluationContext, name: &str) -> Result<Value, ()> {
if !core::ptr::eq((&self.ct) as *const _, eval_context.component.get_vtable() as *const _) {
pub fn get_property(&self, component: ComponentRefPin, name: &str) -> Result<Value, ()> {
if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
return Err(());
}
let x = self.custom_properties.get(name).ok_or(())?;
unsafe {
x.prop.get(
Pin::new_unchecked(&*eval_context.component.as_ptr().add(x.offset)),
eval_context,
)
}
unsafe { x.prop.get(Pin::new_unchecked(&*component.as_ptr().add(x.offset))) }
}
/// Sets an handler for a signal
@ -105,7 +100,7 @@ impl ComponentDescription {
&self,
component: Pin<ComponentRefMut>,
name: &str,
handler: Box<dyn Fn(&EvaluationContext, ())>,
handler: Box<dyn Fn(())>,
) -> Result<(), ()> {
if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
return Err(());
@ -120,14 +115,13 @@ impl ComponentDescription {
///
/// Returns an error if the component is not an instance corresponding to this ComponentDescription,
/// or if the signal with this name does not exist in this component
pub fn emit_signal(&self, eval_context: &EvaluationContext, name: &str) -> Result<(), ()> {
let component = eval_context.component;
pub fn emit_signal(&self, component: ComponentRefPin, name: &str) -> Result<(), ()> {
if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
return Err(());
}
let x = self.custom_signals.get(name).ok_or(())?;
let sig = unsafe { &mut *(component.as_ptr().add(*x) as *mut Signal<()>) };
sig.emit(eval_context, ());
sig.emit(());
Ok(())
}
}