mirror of
https://github.com/slint-ui/slint.git
synced 2025-09-28 04:45:13 +00:00
Lay out items on a path
This works, but it's still missing support for positioning of the layout itself.
This commit is contained in:
parent
8edb9bd7f8
commit
4b75ec0ee8
10 changed files with 309 additions and 139 deletions
|
@ -78,11 +78,14 @@ EvaluationContext evaluation_context_for_root_component(const Component *compone
|
||||||
}
|
}
|
||||||
|
|
||||||
// layouts:
|
// layouts:
|
||||||
using internal::Slice;
|
using internal::Constraint;
|
||||||
using internal::solve_grid_layout;
|
|
||||||
using internal::GridLayoutCellData;
|
using internal::GridLayoutCellData;
|
||||||
using internal::GridLayoutData;
|
using internal::GridLayoutData;
|
||||||
using internal::Constraint;
|
using internal::PathLayoutData;
|
||||||
|
using internal::PathLayoutItemData;
|
||||||
|
using internal::Slice;
|
||||||
|
using internal::solve_grid_layout;
|
||||||
|
using internal::solve_path_layout;
|
||||||
|
|
||||||
// models
|
// models
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,8 @@ pub mod re_exports {
|
||||||
pub use sixtyfps_corelib::abi::slice::Slice;
|
pub use sixtyfps_corelib::abi::slice::Slice;
|
||||||
pub use sixtyfps_corelib::item_tree::visit_item_tree;
|
pub use sixtyfps_corelib::item_tree::visit_item_tree;
|
||||||
pub use sixtyfps_corelib::layout::{
|
pub use sixtyfps_corelib::layout::{
|
||||||
solve_grid_layout, Constraint, GridLayoutCellData, GridLayoutData,
|
solve_grid_layout, solve_path_layout, Constraint, GridLayoutCellData, GridLayoutData,
|
||||||
|
PathLayoutData, PathLayoutItemData,
|
||||||
};
|
};
|
||||||
pub use sixtyfps_corelib::Color;
|
pub use sixtyfps_corelib::Color;
|
||||||
pub use sixtyfps_corelib::ComponentVTable_static;
|
pub use sixtyfps_corelib::ComponentVTable_static;
|
||||||
|
|
|
@ -833,50 +833,7 @@ fn compile_expression(e: &crate::expression_tree::Expression, component: &Rc<Com
|
||||||
panic!("Expression::Object is not a Type::Object")
|
panic!("Expression::Object is not a Type::Object")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PathElements { elements } => match elements {
|
PathElements { elements } => compile_path(elements, component),
|
||||||
crate::expression_tree::Path::Elements(elements) => {
|
|
||||||
let converted_elements: Vec<String> = elements
|
|
||||||
.iter()
|
|
||||||
.map(|element| {
|
|
||||||
let element_initializer = element
|
|
||||||
.element_type
|
|
||||||
.cpp_type
|
|
||||||
.as_ref()
|
|
||||||
.map(|cpp_type| {
|
|
||||||
new_struct_with_bindings(&cpp_type, &element.bindings, component)
|
|
||||||
})
|
|
||||||
.unwrap_or_default();
|
|
||||||
format!(
|
|
||||||
"sixtyfps::PathElement::{}({})",
|
|
||||||
element.element_type.class_name, element_initializer
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
format!(
|
|
||||||
r#"[&](){{
|
|
||||||
sixtyfps::PathElement elements[{}] = {{
|
|
||||||
{}
|
|
||||||
}};
|
|
||||||
return sixtyfps::PathElements(&elements[0], sizeof(elements) / sizeof(elements[0]));
|
|
||||||
}}()"#,
|
|
||||||
converted_elements.len(),
|
|
||||||
converted_elements.join(",")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
crate::expression_tree::Path::Events(events) => {
|
|
||||||
let converted_elements: Vec<String> = compile_path_events(events);
|
|
||||||
format!(
|
|
||||||
r#"[&](){{
|
|
||||||
sixtyfps::PathEvent events[{}] = {{
|
|
||||||
{}
|
|
||||||
}};
|
|
||||||
return sixtyfps::PathElements(&events[0], sizeof(events) / sizeof(events[0]));
|
|
||||||
}}()"#,
|
|
||||||
converted_elements.len(),
|
|
||||||
converted_elements.join(",")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Uncompiled(_) => panic!(),
|
Uncompiled(_) => panic!(),
|
||||||
Invalid => format!("\n#error invalid expression\n"),
|
Invalid => format!("\n#error invalid expression\n"),
|
||||||
}
|
}
|
||||||
|
@ -889,7 +846,7 @@ fn compute_layout(component: &Rc<Component>) -> Vec<String> {
|
||||||
"[[maybe_unused]] auto self = reinterpret_cast<const {ty}*>(component.instance);",
|
"[[maybe_unused]] auto self = reinterpret_cast<const {ty}*>(component.instance);",
|
||||||
ty = component_id(component)
|
ty = component_id(component)
|
||||||
));
|
));
|
||||||
for grid in component.layout_constraints.borrow().0.iter() {
|
for grid in component.layout_constraints.borrow().grids.iter() {
|
||||||
res.push("{".to_owned());
|
res.push("{".to_owned());
|
||||||
res.push(format!(" std::array<sixtyfps::Constraint, {}> row_constr;", grid.row_count()));
|
res.push(format!(" std::array<sixtyfps::Constraint, {}> row_constr;", grid.row_count()));
|
||||||
res.push(format!(" std::array<sixtyfps::Constraint, {}> col_constr;", grid.col_count()));
|
res.push(format!(" std::array<sixtyfps::Constraint, {}> col_constr;", grid.col_count()));
|
||||||
|
@ -943,6 +900,33 @@ fn compute_layout(component: &Rc<Component>) -> Vec<String> {
|
||||||
res.push(" sixtyfps::solve_grid_layout(&grid);".to_owned());
|
res.push(" sixtyfps::solve_grid_layout(&grid);".to_owned());
|
||||||
res.push("}".to_owned());
|
res.push("}".to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for path_layout in component.layout_constraints.borrow().paths.iter() {
|
||||||
|
res.push("{".to_owned());
|
||||||
|
|
||||||
|
res.push(" sixtyfps::PathLayoutItemData items[] = {".to_owned());
|
||||||
|
for item in &path_layout.elements {
|
||||||
|
let p = |n: &str| {
|
||||||
|
if item.borrow().lookup_property(n) == Type::Float32 {
|
||||||
|
format!("&self->{}.{}", item.borrow().id, n)
|
||||||
|
} else {
|
||||||
|
"nullptr".to_owned()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
res.push(format!(" {{ {}, {} }},", p("x"), p("y")));
|
||||||
|
}
|
||||||
|
res.push(" };".to_owned());
|
||||||
|
|
||||||
|
res.push(format!(" auto path = {};", compile_path(&path_layout.path, component)));
|
||||||
|
|
||||||
|
res.push(" sixtyfps::PathLayoutData pl { ".into());
|
||||||
|
res.push(" &path,".to_owned());
|
||||||
|
res.push(" {items, std::size(items)}".to_owned());
|
||||||
|
res.push(" };".to_owned());
|
||||||
|
res.push(" sixtyfps::solve_path_layout(&pl);".to_owned());
|
||||||
|
res.push("}".to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -963,6 +947,53 @@ fn new_path_event(event_name: &str, fields: &[(&str, String)]) -> String {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compile_path(path: &crate::expression_tree::Path, component: &Rc<Component>) -> String {
|
||||||
|
match path {
|
||||||
|
crate::expression_tree::Path::Elements(elements) => {
|
||||||
|
let converted_elements: Vec<String> = elements
|
||||||
|
.iter()
|
||||||
|
.map(|element| {
|
||||||
|
let element_initializer = element
|
||||||
|
.element_type
|
||||||
|
.cpp_type
|
||||||
|
.as_ref()
|
||||||
|
.map(|cpp_type| {
|
||||||
|
new_struct_with_bindings(&cpp_type, &element.bindings, component)
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
format!(
|
||||||
|
"sixtyfps::PathElement::{}({})",
|
||||||
|
element.element_type.class_name, element_initializer
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
format!(
|
||||||
|
r#"[&](){{
|
||||||
|
sixtyfps::PathElement elements[{}] = {{
|
||||||
|
{}
|
||||||
|
}};
|
||||||
|
return sixtyfps::PathElements(&elements[0], sizeof(elements) / sizeof(elements[0]));
|
||||||
|
}}()"#,
|
||||||
|
converted_elements.len(),
|
||||||
|
converted_elements.join(",")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
crate::expression_tree::Path::Events(events) => {
|
||||||
|
let converted_elements: Vec<String> = compile_path_events(events);
|
||||||
|
format!(
|
||||||
|
r#"[&](){{
|
||||||
|
sixtyfps::PathEvent events[{}] = {{
|
||||||
|
{}
|
||||||
|
}};
|
||||||
|
return sixtyfps::PathElements(&events[0], sizeof(events) / sizeof(events[0]));
|
||||||
|
}}()"#,
|
||||||
|
converted_elements.len(),
|
||||||
|
converted_elements.join(",")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn compile_path_events(events: &crate::expression_tree::PathEvents) -> Vec<String> {
|
fn compile_path_events(events: &crate::expression_tree::PathEvents) -> Vec<String> {
|
||||||
use lyon::path::Event;
|
use lyon::path::Event;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::diagnostics::{CompilerDiagnostic, Diagnostics};
|
use crate::diagnostics::{CompilerDiagnostic, Diagnostics};
|
||||||
use crate::expression_tree::{Expression, NamedReference, OperatorClass};
|
use crate::expression_tree::{Expression, NamedReference, OperatorClass, Path};
|
||||||
use crate::object_tree::{Component, ElementRc};
|
use crate::object_tree::{Component, ElementRc};
|
||||||
use crate::parser::Spanned;
|
use crate::parser::Spanned;
|
||||||
use crate::typeregister::Type;
|
use crate::typeregister::Type;
|
||||||
|
@ -678,58 +678,13 @@ fn compile_expression(e: &Expression, component: &Rc<Component>) -> TokenStream
|
||||||
panic!("Expression::Object is not a Type::Object")
|
panic!("Expression::Object is not a Type::Object")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expression::PathElements { elements } => match elements {
|
Expression::PathElements { elements } => compile_path(elements, component),
|
||||||
crate::expression_tree::Path::Elements(elements) => {
|
|
||||||
let converted_elements: Vec<TokenStream> = elements
|
|
||||||
.iter()
|
|
||||||
.map(|element| {
|
|
||||||
let mut bindings = element
|
|
||||||
.bindings
|
|
||||||
.iter()
|
|
||||||
.map(|(property, expr)| {
|
|
||||||
let prop_ident = quote::format_ident!("{}", property);
|
|
||||||
let binding_expr = compile_expression(expr, component);
|
|
||||||
|
|
||||||
quote!(#prop_ident: #binding_expr as _).to_string()
|
|
||||||
})
|
|
||||||
.collect::<Vec<String>>();
|
|
||||||
|
|
||||||
if bindings.len() < element.element_type.properties.len() {
|
|
||||||
bindings.push("..Default::default()".into())
|
|
||||||
}
|
|
||||||
|
|
||||||
let bindings = bindings.join(",");
|
|
||||||
|
|
||||||
let ctor_format_string = element
|
|
||||||
.element_type
|
|
||||||
.rust_type_constructor
|
|
||||||
.as_ref()
|
|
||||||
.expect(
|
|
||||||
"Unexpected error in type registry: path element is lacking rust type name",
|
|
||||||
);
|
|
||||||
|
|
||||||
ctor_format_string
|
|
||||||
.replace("{}", &bindings)
|
|
||||||
.parse()
|
|
||||||
.expect("Error parsing rust path element constructor")
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
quote!(sixtyfps::re_exports::PathElements::SharedElements(
|
|
||||||
sixtyfps::re_exports::SharedArray::<sixtyfps::re_exports::PathElement>::from(&[#(#converted_elements),*])
|
|
||||||
))
|
|
||||||
}
|
|
||||||
crate::expression_tree::Path::Events(events) => {
|
|
||||||
let events = compile_path_events(events);
|
|
||||||
quote!(sixtyfps::re_exports::PathElements::PathEvents(#events))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_layout(component: &Component) -> TokenStream {
|
fn compute_layout(component: &Rc<Component>) -> TokenStream {
|
||||||
let mut layouts = vec![];
|
let mut layouts = vec![];
|
||||||
for x in component.layout_constraints.borrow().0.iter() {
|
for x in component.layout_constraints.borrow().grids.iter() {
|
||||||
let within = quote::format_ident!("{}", x.within.borrow().id);
|
let within = quote::format_ident!("{}", x.within.borrow().id);
|
||||||
let within_ty =
|
let within_ty =
|
||||||
quote::format_ident!("{}", x.within.borrow().base_type.as_builtin().class_name);
|
quote::format_ident!("{}", x.within.borrow().base_type.as_builtin().class_name);
|
||||||
|
@ -784,6 +739,39 @@ fn compute_layout(component: &Component) -> TokenStream {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for path_layout in component.layout_constraints.borrow().paths.iter() {
|
||||||
|
let items = path_layout
|
||||||
|
.elements
|
||||||
|
.iter()
|
||||||
|
.map(|elem| {
|
||||||
|
let e = quote::format_ident!("{}", elem.borrow().id);
|
||||||
|
let p = |n: &str| {
|
||||||
|
if elem.borrow().lookup_property(n) == Type::Float32 {
|
||||||
|
let n = quote::format_ident!("{}", n);
|
||||||
|
quote! {Some(&self.#e.#n)}
|
||||||
|
} else {
|
||||||
|
quote! {None}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let x = p("x");
|
||||||
|
let y = p("y");
|
||||||
|
quote!(PathLayoutItemData {
|
||||||
|
x: #x,
|
||||||
|
y: #y,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let path = compile_path(&path_layout.path, &component);
|
||||||
|
|
||||||
|
layouts.push(quote! {
|
||||||
|
solve_path_layout(&PathLayoutData {
|
||||||
|
items: Slice::from_slice(&[#( #items ),*]),
|
||||||
|
elements: &#path,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
fn layout_info(self: ::core::pin::Pin<&Self>) -> sixtyfps::re_exports::LayoutInfo {
|
fn layout_info(self: ::core::pin::Pin<&Self>) -> sixtyfps::re_exports::LayoutInfo {
|
||||||
todo!("Implement in rust.rs")
|
todo!("Implement in rust.rs")
|
||||||
|
@ -848,3 +836,52 @@ fn compile_path_events(events: &crate::expression_tree::PathEvents) -> TokenStre
|
||||||
|
|
||||||
quote!(sixtyfps::re_exports::SharedArray::<sixtyfps::re_exports::PathEvent>::from(&[#(#converted_events),*]))
|
quote!(sixtyfps::re_exports::SharedArray::<sixtyfps::re_exports::PathEvent>::from(&[#(#converted_events),*]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compile_path(path: &Path, component: &Rc<Component>) -> TokenStream {
|
||||||
|
match path {
|
||||||
|
Path::Elements(elements) => {
|
||||||
|
let converted_elements: Vec<TokenStream> = elements
|
||||||
|
.iter()
|
||||||
|
.map(|element| {
|
||||||
|
let mut bindings = element
|
||||||
|
.bindings
|
||||||
|
.iter()
|
||||||
|
.map(|(property, expr)| {
|
||||||
|
let prop_ident = quote::format_ident!("{}", property);
|
||||||
|
let binding_expr = compile_expression(expr, component);
|
||||||
|
|
||||||
|
quote!(#prop_ident: #binding_expr as _).to_string()
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
|
if bindings.len() < element.element_type.properties.len() {
|
||||||
|
bindings.push("..Default::default()".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
let bindings = bindings.join(",");
|
||||||
|
|
||||||
|
let ctor_format_string = element
|
||||||
|
.element_type
|
||||||
|
.rust_type_constructor
|
||||||
|
.as_ref()
|
||||||
|
.expect(
|
||||||
|
"Unexpected error in type registry: path element is lacking rust type name",
|
||||||
|
);
|
||||||
|
|
||||||
|
ctor_format_string
|
||||||
|
.replace("{}", &bindings)
|
||||||
|
.parse()
|
||||||
|
.expect("Error parsing rust path element constructor")
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
quote!(sixtyfps::re_exports::PathElements::SharedElements(
|
||||||
|
sixtyfps::re_exports::SharedArray::<sixtyfps::re_exports::PathElement>::from(&[#(#converted_elements),*])
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Path::Events(events) => {
|
||||||
|
let events = compile_path_events(events);
|
||||||
|
quote!(sixtyfps::re_exports::PathElements::PathEvents(#events))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
//! Datastructures used to represent layouts in the compiler
|
//! Datastructures used to represent layouts in the compiler
|
||||||
|
|
||||||
|
use crate::expression_tree::Path;
|
||||||
use crate::object_tree::ElementRc;
|
use crate::object_tree::ElementRc;
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct LayoutConstraints(pub Vec<GridLayout>);
|
pub struct LayoutConstraints {
|
||||||
|
pub grids: Vec<GridLayout>,
|
||||||
|
pub paths: Vec<PathLayout>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Internal representation of a grid layout
|
/// Internal representation of a grid layout
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -24,3 +28,10 @@ impl GridLayout {
|
||||||
self.elems.len()
|
self.elems.len()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Internal representation of a path layout
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PathLayout {
|
||||||
|
pub path: Path,
|
||||||
|
pub elements: Vec<ElementRc>,
|
||||||
|
}
|
||||||
|
|
|
@ -71,14 +71,13 @@ pub fn compile_paths(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let mut children = std::mem::take(&mut elem.children);
|
let new_children = Vec::with_capacity(elem.children.len());
|
||||||
|
let old_children = std::mem::replace(&mut elem.children, new_children);
|
||||||
|
|
||||||
crate::expression_tree::Path::Elements(
|
let mut path_data = Vec::new();
|
||||||
children
|
|
||||||
.iter_mut()
|
for child in old_children {
|
||||||
.filter_map(|child| {
|
let element_name = &child.borrow().base_type.as_builtin().class_name.clone();
|
||||||
let mut child = child.borrow_mut();
|
|
||||||
let element_name = &child.base_type.as_builtin().class_name;
|
|
||||||
|
|
||||||
if let Some(path_element) = element_types.get(element_name) {
|
if let Some(path_element) = element_types.get(element_name) {
|
||||||
let element_type = match path_element {
|
let element_type = match path_element {
|
||||||
|
@ -88,14 +87,13 @@ pub fn compile_paths(
|
||||||
element_name
|
element_name
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
let bindings = std::mem::take(&mut child.bindings);
|
let bindings = std::mem::take(&mut child.borrow_mut().bindings);
|
||||||
Some(PathElement { element_type, bindings })
|
path_data.push(PathElement { element_type, bindings });
|
||||||
} else {
|
} else {
|
||||||
None
|
elem.children.push(child);
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.collect(),
|
crate::expression_tree::Path::Elements(path_data)
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
elem.bindings.insert("elements".into(), Expression::PathElements { elements: path_data });
|
elem.bindings.insert("elements".into(), Expression::PathElements { elements: path_data });
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use crate::diagnostics::Diagnostics;
|
use crate::diagnostics::Diagnostics;
|
||||||
|
|
||||||
|
use crate::expression_tree::*;
|
||||||
use crate::layout::*;
|
use crate::layout::*;
|
||||||
use crate::object_tree::*;
|
use crate::object_tree::*;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -14,7 +15,7 @@ pub fn lower_layouts(component: &Rc<Component>, diag: &mut Diagnostics) {
|
||||||
let old_children = std::mem::replace(&mut elem.children, new_children);
|
let old_children = std::mem::replace(&mut elem.children, new_children);
|
||||||
|
|
||||||
for child in old_children {
|
for child in old_children {
|
||||||
let is_layout =
|
let is_grid_layout =
|
||||||
if let crate::typeregister::Type::Builtin(be) = &child.borrow().base_type {
|
if let crate::typeregister::Type::Builtin(be) = &child.borrow().base_type {
|
||||||
assert!(be.class_name != "Row"); // Caught at element lookup time
|
assert!(be.class_name != "Row"); // Caught at element lookup time
|
||||||
be.class_name == "GridLayout"
|
be.class_name == "GridLayout"
|
||||||
|
@ -22,7 +23,14 @@ pub fn lower_layouts(component: &Rc<Component>, diag: &mut Diagnostics) {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_layout {
|
let is_path_layout =
|
||||||
|
if let crate::typeregister::Type::Builtin(be) = &child.borrow().base_type {
|
||||||
|
be.class_name == "PathLayout"
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_grid_layout {
|
||||||
let mut grid = GridLayout { within: elem_.clone(), elems: Default::default() };
|
let mut grid = GridLayout { within: elem_.clone(), elems: Default::default() };
|
||||||
let mut row = 0;
|
let mut row = 0;
|
||||||
let mut col = 0;
|
let mut col = 0;
|
||||||
|
@ -53,7 +61,26 @@ pub fn lower_layouts(component: &Rc<Component>, diag: &mut Diagnostics) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
component.optimized_elements.borrow_mut().push(child.clone());
|
component.optimized_elements.borrow_mut().push(child.clone());
|
||||||
component.layout_constraints.borrow_mut().0.push(grid);
|
component.layout_constraints.borrow_mut().grids.push(grid);
|
||||||
|
continue;
|
||||||
|
} else if is_path_layout {
|
||||||
|
let layout_elem = child;
|
||||||
|
let layout_children = std::mem::take(&mut layout_elem.borrow_mut().children);
|
||||||
|
elem.children.extend(layout_children.iter().cloned());
|
||||||
|
component.optimized_elements.borrow_mut().push(layout_elem.clone());
|
||||||
|
let path_elements_expr = match layout_elem.borrow_mut().bindings.remove("elements")
|
||||||
|
{
|
||||||
|
Some(Expression::PathElements { elements }) => elements,
|
||||||
|
_ => {
|
||||||
|
diag.push_error("Internal error: elements binding in PathLayout does not contain path elements expression".into(), layout_elem.borrow().span());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
component
|
||||||
|
.layout_constraints
|
||||||
|
.borrow_mut()
|
||||||
|
.paths
|
||||||
|
.push(PathLayout { elements: layout_children, path: path_elements_expr });
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
elem.children.push(child);
|
elem.children.push(child);
|
||||||
|
|
|
@ -500,7 +500,7 @@ unsafe extern "C" fn compute_layout(component: ComponentRefPin, eval_context: &E
|
||||||
let component_type =
|
let component_type =
|
||||||
&*(component.get_vtable() as *const ComponentVTable as *const ComponentDescription);
|
&*(component.get_vtable() as *const ComponentVTable as *const ComponentDescription);
|
||||||
|
|
||||||
for it in &component_type.original.layout_constraints.borrow().0 {
|
for it in &component_type.original.layout_constraints.borrow().grids {
|
||||||
use sixtyfps_corelib::layout::*;
|
use sixtyfps_corelib::layout::*;
|
||||||
|
|
||||||
let mut row_constraint = vec![];
|
let mut row_constraint = vec![];
|
||||||
|
@ -557,6 +557,32 @@ unsafe extern "C" fn compute_layout(component: ComponentRefPin, eval_context: &E
|
||||||
cells: Slice::from(cells.as_slice()),
|
cells: Slice::from(cells.as_slice()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for it in &component_type.original.layout_constraints.borrow().paths {
|
||||||
|
use sixtyfps_corelib::layout::*;
|
||||||
|
|
||||||
|
let items = it
|
||||||
|
.elements
|
||||||
|
.iter()
|
||||||
|
.map(|elem| {
|
||||||
|
let info = &component_type.items[elem.borrow().id.as_str()];
|
||||||
|
let get_prop = |name| {
|
||||||
|
info.rtti.properties.get(name).map(|p| {
|
||||||
|
&*(component.as_ptr().add(info.offset).add(p.offset())
|
||||||
|
as *const Property<f32>)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
PathLayoutItemData { x: get_prop("x"), y: get_prop("y") }
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let path_elements = eval::convert_path(&it.path, component_type, eval_context);
|
||||||
|
|
||||||
|
solve_path_layout(&PathLayoutData {
|
||||||
|
items: Slice::from(items.as_slice()),
|
||||||
|
elements: &path_elements,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the component description from a ComponentRef
|
/// Get the component description from a ComponentRef
|
||||||
|
|
|
@ -323,22 +323,7 @@ pub fn eval_expression(
|
||||||
.collect(),
|
.collect(),
|
||||||
),
|
),
|
||||||
Expression::PathElements { elements } => {
|
Expression::PathElements { elements } => {
|
||||||
match elements {
|
Value::PathElements(convert_path(elements, component_type, eval_context))
|
||||||
ExprPath::Elements(elements) => Value::PathElements(PathElements::SharedElements(
|
|
||||||
sixtyfps_corelib::SharedArray::<
|
|
||||||
sixtyfps_corelib::abi::datastructures::PathElement,
|
|
||||||
>::from_iter(elements.iter().map(|element| {
|
|
||||||
convert_path_element(element, component_type, eval_context)
|
|
||||||
})),
|
|
||||||
)),
|
|
||||||
ExprPath::Events(events) => {
|
|
||||||
Value::PathElements(PathElements::PathEvents(sixtyfps_corelib::SharedArray::<
|
|
||||||
sixtyfps_corelib::abi::datastructures::PathEvent,
|
|
||||||
>::from_iter(
|
|
||||||
events.iter().map(|event| event.into()),
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -378,6 +363,29 @@ pub fn new_struct_with_bindings<
|
||||||
element
|
element
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn convert_path(
|
||||||
|
path: &ExprPath,
|
||||||
|
component_type: &crate::ComponentDescription,
|
||||||
|
eval_context: &corelib::EvaluationContext,
|
||||||
|
) -> sixtyfps_corelib::abi::datastructures::PathElements {
|
||||||
|
match path {
|
||||||
|
ExprPath::Elements(elements) => {
|
||||||
|
PathElements::SharedElements(sixtyfps_corelib::SharedArray::<
|
||||||
|
sixtyfps_corelib::abi::datastructures::PathElement,
|
||||||
|
>::from_iter(
|
||||||
|
elements
|
||||||
|
.iter()
|
||||||
|
.map(|element| convert_path_element(element, component_type, eval_context)),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
ExprPath::Events(events) => PathElements::PathEvents(sixtyfps_corelib::SharedArray::<
|
||||||
|
sixtyfps_corelib::abi::datastructures::PathEvent,
|
||||||
|
>::from_iter(
|
||||||
|
events.iter().map(|event| event.into()),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn convert_path_element(
|
fn convert_path_element(
|
||||||
expr_element: &ExprPathElement,
|
expr_element: &ExprPathElement,
|
||||||
component_type: &crate::ComponentDescription,
|
component_type: &crate::ComponentDescription,
|
||||||
|
|
28
tests/cases/path_layout.60
Normal file
28
tests/cases/path_layout.60
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
TestCase := Rectangle {
|
||||||
|
|
||||||
|
Path {
|
||||||
|
commands: "M 100 300 Q 150 50 250 150 C 250 300 300 300 300 450 A 50 50 0 1 0 450 450 L 550 300";
|
||||||
|
stroke_color: black;
|
||||||
|
stroke_width: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
PathLayout {
|
||||||
|
commands: "M 100 300 Q 150 50 250 150 C 250 300 300 300 300 450 A 50 50 0 1 0 450 450 L 550 300";
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "First item on path";
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Second item on path";
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Third item on path";
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue