diff --git a/examples/cpptest/hello.60 b/examples/cpptest/hello.60 index 738c635b4..eaf75774a 100644 --- a/examples/cpptest/hello.60 +++ b/examples/cpptest/hello.60 @@ -77,7 +77,8 @@ Hello := Rectangle { color: black; } } - counter := Text { x: 100; y: 300; text: "0"; color: black; } + property counter; + counter_label := Text { x: 100; y: 300; text: "0"; color: black; } ButtonRectangle { color: 4289374890; x: 50; diff --git a/examples/cpptest/main.cpp b/examples/cpptest/main.cpp index df032566e..891dd3923 100644 --- a/examples/cpptest/main.cpp +++ b/examples/cpptest/main.cpp @@ -4,24 +4,24 @@ int main() { static Hello component; - static int counter = 0; - component._foobar.set_handler([](auto...){ std::cout << "Hello from C++" << std::endl; }); component._plus_clicked.set_handler([](auto...){ - counter += 1; + auto &counter = component.property_0_counter; + counter.set(counter.get() + 1); // FIXME: this _13 is an internal detail and should be private anyway. We muse use some // alias or way to expose the property (same for the _ before signals) - component.counter_13.text.set(std::string_view(std::to_string(counter))); - std::cout << "PLUS: " << std::string_view(component.counter_13.text.get()) << std::endl; + component.counter_label_13.text.set(std::string_view(std::to_string(counter.get()))); + std::cout << "PLUS: " << std::string_view(component.counter_label_13.text.get()) << std::endl; }); component._minus_clicked.set_handler([](auto...){ - counter -= 1; - component.counter_13.text.set(std::string_view(std::to_string(counter))); - std::cout << "MINUS: " << std::string_view(component.counter_13.text.get()) << std::endl; + auto &counter = component.property_0_counter; + counter.set(counter.get() - 1); + component.counter_label_13.text.set(std::string_view(std::to_string(counter.get()))); + std::cout << "MINUS: " << std::string_view(component.counter_label_13.text.get()) << std::endl; }); sixtyfps::run(&component); diff --git a/sixtyfps_compiler/generator/cpp.rs b/sixtyfps_compiler/generator/cpp.rs index 81553ce90..3e6230fa0 100644 --- a/sixtyfps_compiler/generator/cpp.rs +++ b/sixtyfps_compiler/generator/cpp.rs @@ -121,12 +121,35 @@ mod cpp_ast { writeln!(f, ";") } } + + pub trait CppType { + fn cpp_type(&self) -> Option<&str>; + } } use crate::lower::{LoweredComponent, LoweredItem}; +use crate::typeregister::Type; use cpp_ast::*; -fn handle_item(item: &LoweredItem, main_struct: &mut Struct, init: &mut Vec) { +impl CppType for Type { + fn cpp_type(&self) -> Option<&str> { + match self { + Type::Float32 => Some("float"), + Type::Int32 => Some("int"), + Type::String => Some("sixtyfps::SharedString"), + Type::Color => Some("uint32_t"), + Type::Bool => Some("bool"), + _ => None, + } + } +} + +fn handle_item( + item: &LoweredItem, + global_properties: &Vec, + main_struct: &mut Struct, + init: &mut Vec, +) { main_struct.members.push(Declaration::Var(Var { ty: format!("sixtyfps::{}", item.native_type.class_name), name: item.id.clone(), @@ -135,13 +158,19 @@ fn handle_item(item: &LoweredItem, main_struct: &mut Struct, init: &mut Vec format!(r#"sixtyfps::SharedString("{}")"#, s.escape_default()), NumberLiteral(n) => n.to_string(), _ => format!("\n#error: unsupported expression {:?}\n", i), }; - format!("{id}.{prop}.set({init});", id = id, prop = s, init = init) + format!("{cpp_prop}.set({init});", cpp_prop = cpp_prop, init = init) })); init.extend(item.connect_signals.iter().map(|(s, fwd)| { format!( @@ -151,7 +180,7 @@ fn handle_item(item: &LoweredItem, main_struct: &mut Struct, init: &mut Vec impl std::fmt::Display { let mut main_struct = Struct { name: component.id.clone(), ..Default::default() }; + let (declared_property_members, declared_property_vars): (Vec, Vec) = + component + .property_declarations + .iter() + .enumerate() + .map(|(index, property)| { + let cpp_name: String = format!("property_{}_{}", index, property.name_hint).into(); + ( + cpp_name.clone(), + Declaration::Var(Var { + ty: format!( + "sixtyfps::Property<{}>", + property.property_type.cpp_type().expect("cannot convert type to C++") + ), + name: cpp_name, + init: None, + }), + ) + }) + .unzip(); + + main_struct.members.extend(declared_property_vars); + let mut init = Vec::new(); - handle_item(&component.root_item, &mut main_struct, &mut init); + handle_item(&component.root_item, &declared_property_members, &mut main_struct, &mut init); main_struct.members.extend(component.signals_declarations.iter().map(|s| { Declaration::Var(Var { ty: "sixtyfps::Signal".into(), name: s.clone(), init: None }) diff --git a/sixtyfps_compiler/lower.rs b/sixtyfps_compiler/lower.rs index 3b3b3bbfa..ff9955ec6 100644 --- a/sixtyfps_compiler/lower.rs +++ b/sixtyfps_compiler/lower.rs @@ -15,6 +15,8 @@ pub struct NativeItemType { pub class_name: String, } +pub type LoweredPropertyDeclarationIndex = usize; + #[derive(Default, Debug)] pub struct LoweredItem { pub id: String, @@ -22,6 +24,7 @@ pub struct LoweredItem { pub init_properties: HashMap, /// Right now we only allow forwarding and this connect with the signal in the root pub connect_signals: HashMap, + pub property_declarations: HashMap, pub children: Vec, } @@ -31,6 +34,7 @@ pub struct LoweredComponent { pub root_item: LoweredItem, pub signals_declarations: Vec, + pub property_declarations: Vec, } impl LoweredComponent { @@ -40,6 +44,7 @@ impl LoweredComponent { id: component.id.clone(), root_item: LoweredComponent::lower_item(&*component.root_element.borrow(), &mut state), signals_declarations: state.signals, + property_declarations: state.property_declarations, } } @@ -72,6 +77,16 @@ impl LoweredComponent { let current_component_id = state.current_component_id.clone(); let format_signal = |name| format!("{}_{}", current_component_id, name); state.signals.extend(element.signals_declaration.iter().map(format_signal)); + + for (prop_name, property_type) in element.property_declarations.iter() { + let component_global_index = state.property_declarations.len(); + lowered.property_declarations.insert(prop_name.clone(), component_global_index); + state.property_declarations.push(LoweredPropertyDeclaration { + property_type: property_type.clone(), + name_hint: prop_name.clone(), + }); + } + for (k, e) in element.bindings.iter() { if let Expression::SignalReference { name, .. } = e { lowered.connect_signals.insert( @@ -89,6 +104,12 @@ impl LoweredComponent { } } +#[derive(Debug)] +pub struct LoweredPropertyDeclaration { + pub property_type: Type, + pub name_hint: String, +} + #[derive(Default)] struct LowererState { /// The count of item to create the ids @@ -97,4 +118,5 @@ struct LowererState { current_component_id: String, signals: Vec, + property_declarations: Vec, }