mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-01 06:11:16 +00:00
336 lines
13 KiB
Rust
336 lines
13 KiB
Rust
/* LICENSE BEGIN
|
|
This file is part of the SixtyFPS Project -- https://sixtyfps.io
|
|
Copyright (c) 2020 Olivier Goffart <olivier.goffart@sixtyfps.io>
|
|
Copyright (c) 2020 Simon Hausmann <simon.hausmann@sixtyfps.io>
|
|
|
|
SPDX-License-Identifier: GPL-3.0-only
|
|
This file is also available under commercial licensing terms.
|
|
Please contact info@sixtyfps.io for more information.
|
|
LICENSE END */
|
|
|
|
use crate::dynamic_component::InstanceRef;
|
|
use crate::eval::{self, ComponentInstance, EvalLocalContext};
|
|
use crate::Value;
|
|
use sixtyfps_compilerlib::expression_tree::Expression;
|
|
use sixtyfps_compilerlib::langtype::Type;
|
|
use sixtyfps_compilerlib::layout::{Layout, LayoutConstraints, LayoutGeometry};
|
|
use sixtyfps_compilerlib::namedreference::NamedReference;
|
|
use sixtyfps_compilerlib::object_tree::ElementRc;
|
|
use sixtyfps_corelib::layout as core_layout;
|
|
use sixtyfps_corelib::model::RepeatedComponent;
|
|
use sixtyfps_corelib::slice::Slice;
|
|
use sixtyfps_corelib::window::ComponentWindow;
|
|
use std::convert::TryInto;
|
|
|
|
pub(crate) fn compute_layout_info(lay: &Layout, local_context: &mut EvalLocalContext) -> Value {
|
|
let component = match local_context.component_instance {
|
|
ComponentInstance::InstanceRef(c) => c,
|
|
ComponentInstance::GlobalComponent(_) => panic!("Cannot compute layout from a Global"),
|
|
};
|
|
let expr_eval = |nr: &NamedReference| -> f32 {
|
|
eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
|
|
};
|
|
match lay {
|
|
Layout::GridLayout(grid_layout) => {
|
|
let cells = grid_layout_data(grid_layout, component, &expr_eval);
|
|
let (padding, spacing) = padding_and_spacing(&grid_layout.geometry, &expr_eval);
|
|
core_layout::grid_layout_info(Slice::from(cells.as_slice()), spacing, &padding).into()
|
|
}
|
|
Layout::BoxLayout(box_layout) => {
|
|
let (cells, alignment) = box_layout_data(box_layout, component, &expr_eval, None);
|
|
let (padding, spacing) = padding_and_spacing(&box_layout.geometry, &expr_eval);
|
|
core_layout::box_layout_info(
|
|
Slice::from(cells.as_slice()),
|
|
spacing,
|
|
&padding,
|
|
alignment,
|
|
box_layout.is_horizontal,
|
|
)
|
|
.into()
|
|
}
|
|
Layout::PathLayout(_) => unimplemented!(),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn solve_layout(lay: &Layout, local_context: &mut EvalLocalContext) -> Value {
|
|
let component = match local_context.component_instance {
|
|
ComponentInstance::InstanceRef(c) => c,
|
|
ComponentInstance::GlobalComponent(_) => panic!("Cannot compute layout from a Global"),
|
|
};
|
|
let expr_eval = |nr: &NamedReference| -> f32 {
|
|
eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
|
|
};
|
|
|
|
match lay {
|
|
Layout::GridLayout(grid_layout) => {
|
|
let cells = grid_layout_data(grid_layout, component, &expr_eval);
|
|
let (padding, spacing) = padding_and_spacing(&grid_layout.geometry, &expr_eval);
|
|
|
|
Value::LayoutCache(core_layout::solve_grid_layout(&core_layout::GridLayoutData {
|
|
width: grid_layout
|
|
.geometry
|
|
.rect
|
|
.width_reference
|
|
.as_ref()
|
|
.map(expr_eval)
|
|
.unwrap_or(0.),
|
|
height: grid_layout
|
|
.geometry
|
|
.rect
|
|
.height_reference
|
|
.as_ref()
|
|
.map(expr_eval)
|
|
.unwrap_or(0.),
|
|
x: 0.,
|
|
y: 0.,
|
|
spacing,
|
|
padding: &padding,
|
|
cells: Slice::from(cells.as_slice()),
|
|
}))
|
|
}
|
|
Layout::BoxLayout(box_layout) => {
|
|
let mut repeated_indices = Vec::new();
|
|
let (cells, alignment) =
|
|
box_layout_data(box_layout, component, &expr_eval, Some(&mut repeated_indices));
|
|
let (padding, spacing) = padding_and_spacing(&box_layout.geometry, &expr_eval);
|
|
core_layout::solve_box_layout(
|
|
&core_layout::BoxLayoutData {
|
|
width: box_layout
|
|
.geometry
|
|
.rect
|
|
.width_reference
|
|
.as_ref()
|
|
.map(expr_eval)
|
|
.unwrap_or(0.),
|
|
height: box_layout
|
|
.geometry
|
|
.rect
|
|
.height_reference
|
|
.as_ref()
|
|
.map(expr_eval)
|
|
.unwrap_or(0.),
|
|
x: 0.,
|
|
y: 0.,
|
|
spacing,
|
|
padding: &padding,
|
|
alignment,
|
|
cells: Slice::from(cells.as_slice()),
|
|
},
|
|
box_layout.is_horizontal,
|
|
Slice::from(repeated_indices.as_slice()),
|
|
)
|
|
.into()
|
|
}
|
|
Layout::PathLayout(path_layout) => {
|
|
let repeated_indices = repeater_indices(&path_layout.elements, component);
|
|
core_layout::solve_path_layout(
|
|
&core_layout::PathLayoutData {
|
|
width: path_layout.rect.width_reference.as_ref().map(expr_eval).unwrap_or(0.),
|
|
height: path_layout.rect.height_reference.as_ref().map(expr_eval).unwrap_or(0.),
|
|
x: 0.,
|
|
y: 0.,
|
|
elements: &eval::eval_expression(
|
|
&Expression::PathElements { elements: path_layout.path.clone() },
|
|
local_context,
|
|
)
|
|
.try_into()
|
|
.unwrap(),
|
|
offset: path_layout.offset_reference.as_ref().map_or(0., expr_eval),
|
|
item_count: path_layout.elements.len() as u32,
|
|
},
|
|
Slice::from(repeated_indices.as_slice()),
|
|
)
|
|
.into()
|
|
}
|
|
}
|
|
}
|
|
|
|
fn padding_and_spacing(
|
|
layout_geometry: &LayoutGeometry,
|
|
expr_eval: &impl Fn(&NamedReference) -> f32,
|
|
) -> (core_layout::Padding, f32) {
|
|
let spacing = layout_geometry.spacing.as_ref().map_or(0., expr_eval);
|
|
let padding = core_layout::Padding {
|
|
left: layout_geometry.padding.left.as_ref().map_or(0., expr_eval),
|
|
right: layout_geometry.padding.right.as_ref().map_or(0., expr_eval),
|
|
top: layout_geometry.padding.top.as_ref().map_or(0., expr_eval),
|
|
bottom: layout_geometry.padding.bottom.as_ref().map_or(0., expr_eval),
|
|
};
|
|
(padding, spacing)
|
|
}
|
|
|
|
/// return the celldata, the padding, and the spacing of a grid layout
|
|
fn grid_layout_data(
|
|
grid_layout: &sixtyfps_compilerlib::layout::GridLayout,
|
|
component: InstanceRef,
|
|
expr_eval: &impl Fn(&NamedReference) -> f32,
|
|
) -> Vec<core_layout::GridLayoutCellData> {
|
|
let cells = grid_layout
|
|
.elems
|
|
.iter()
|
|
.map(|cell| {
|
|
let mut layout_info = get_layout_info(
|
|
&cell.item.element,
|
|
component,
|
|
&eval::window_ref(component).unwrap(),
|
|
);
|
|
fill_layout_info_constraints(&mut layout_info, &cell.item.constraints, &expr_eval);
|
|
core_layout::GridLayoutCellData {
|
|
col: cell.col,
|
|
row: cell.row,
|
|
colspan: cell.colspan,
|
|
rowspan: cell.rowspan,
|
|
constraint: layout_info,
|
|
}
|
|
})
|
|
.collect::<Vec<_>>();
|
|
cells
|
|
}
|
|
|
|
fn box_layout_data(
|
|
box_layout: &sixtyfps_compilerlib::layout::BoxLayout,
|
|
component: InstanceRef,
|
|
expr_eval: &impl Fn(&NamedReference) -> f32,
|
|
mut repeater_indices: Option<&mut Vec<u32>>,
|
|
) -> (Vec<core_layout::BoxLayoutCellData>, core_layout::LayoutAlignment) {
|
|
let window = eval::window_ref(component).unwrap();
|
|
let mut cells = Vec::with_capacity(box_layout.elems.len());
|
|
for cell in &box_layout.elems {
|
|
if cell.element.borrow().repeated.is_some() {
|
|
generativity::make_guard!(guard);
|
|
let rep = crate::dynamic_component::get_repeater_by_name(
|
|
component,
|
|
cell.element.borrow().id.as_str(),
|
|
guard,
|
|
);
|
|
rep.0.as_ref().ensure_updated(|| {
|
|
let instance = crate::dynamic_component::instantiate(
|
|
rep.1.clone(),
|
|
Some(component.borrow()),
|
|
window.clone(),
|
|
);
|
|
instance.run_setup_code();
|
|
instance
|
|
});
|
|
let component_vec = rep.0.as_ref().components_vec();
|
|
if let Some(ri) = repeater_indices.as_mut() {
|
|
ri.push(cells.len() as _);
|
|
ri.push(component_vec.len() as _);
|
|
}
|
|
cells.extend(component_vec.iter().map(|x| x.as_pin_ref().box_layout_data()));
|
|
} else {
|
|
let mut layout_info = get_layout_info(&cell.element, component, &window);
|
|
fill_layout_info_constraints(&mut layout_info, &cell.constraints, &expr_eval);
|
|
cells.push(core_layout::BoxLayoutCellData { constraint: layout_info });
|
|
}
|
|
}
|
|
let alignment = box_layout
|
|
.geometry
|
|
.alignment
|
|
.as_ref()
|
|
.map(|nr| {
|
|
eval::load_property(component, &nr.element(), nr.name())
|
|
.unwrap()
|
|
.try_into()
|
|
.unwrap_or_default()
|
|
})
|
|
.unwrap_or_default();
|
|
(cells, alignment)
|
|
}
|
|
|
|
fn repeater_indices(children: &[ElementRc], component: InstanceRef) -> Vec<u32> {
|
|
let window = eval::window_ref(component).unwrap();
|
|
|
|
let mut idx = 0;
|
|
let mut ri = Vec::new();
|
|
for e in children {
|
|
if e.borrow().repeated.is_some() {
|
|
generativity::make_guard!(guard);
|
|
let rep = crate::dynamic_component::get_repeater_by_name(
|
|
component,
|
|
e.borrow().id.as_str(),
|
|
guard,
|
|
);
|
|
rep.0.as_ref().ensure_updated(|| {
|
|
let instance = crate::dynamic_component::instantiate(
|
|
rep.1.clone(),
|
|
Some(component.borrow()),
|
|
window.clone(),
|
|
);
|
|
instance.run_setup_code();
|
|
instance
|
|
});
|
|
let component_vec = rep.0.as_ref().components_vec();
|
|
ri.push(idx);
|
|
ri.push(component_vec.len() as _);
|
|
idx += component_vec.len() as u32;
|
|
} else {
|
|
idx += 1;
|
|
}
|
|
}
|
|
ri
|
|
}
|
|
|
|
pub(crate) fn fill_layout_info_constraints(
|
|
layout_info: &mut core_layout::LayoutInfo,
|
|
constraints: &LayoutConstraints,
|
|
expr_eval: &impl Fn(&NamedReference) -> f32,
|
|
) {
|
|
let is_percent =
|
|
|nr: &NamedReference| Expression::PropertyReference(nr.clone()).ty() == Type::Percent;
|
|
constraints.minimum_width.as_ref().map(|e| {
|
|
if !is_percent(e) {
|
|
layout_info.min_width = expr_eval(e)
|
|
} else {
|
|
layout_info.min_width_percent = expr_eval(e)
|
|
}
|
|
});
|
|
constraints.maximum_width.as_ref().map(|e| {
|
|
if !is_percent(e) {
|
|
layout_info.max_width = expr_eval(e)
|
|
} else {
|
|
layout_info.max_width_percent = expr_eval(e)
|
|
}
|
|
});
|
|
constraints.minimum_height.as_ref().map(|e| {
|
|
if !is_percent(e) {
|
|
layout_info.min_height = expr_eval(e)
|
|
} else {
|
|
layout_info.min_height_percent = expr_eval(e)
|
|
}
|
|
});
|
|
constraints.maximum_height.as_ref().map(|e| {
|
|
if !is_percent(e) {
|
|
layout_info.max_height = expr_eval(e)
|
|
} else {
|
|
layout_info.max_height_percent = expr_eval(e)
|
|
}
|
|
});
|
|
constraints.preferred_width.as_ref().map(|e| {
|
|
layout_info.preferred_width = expr_eval(e);
|
|
});
|
|
constraints.preferred_height.as_ref().map(|e| {
|
|
layout_info.preferred_height = expr_eval(e);
|
|
});
|
|
constraints.horizontal_stretch.as_ref().map(|e| layout_info.horizontal_stretch = expr_eval(e));
|
|
constraints.vertical_stretch.as_ref().map(|e| layout_info.vertical_stretch = expr_eval(e));
|
|
}
|
|
|
|
/// Get the layout info for an element based on the layout_info_prop or the builtin item layouting_info
|
|
pub(crate) fn get_layout_info(
|
|
elem: &ElementRc,
|
|
component: InstanceRef,
|
|
window: &ComponentWindow,
|
|
) -> core_layout::LayoutInfo {
|
|
let elem = elem.borrow();
|
|
if let Some(nr) = &elem.layout_info_prop {
|
|
eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
|
|
} else {
|
|
let item = &component
|
|
.component_type
|
|
.items
|
|
.get(elem.id.as_str())
|
|
.unwrap_or_else(|| panic!("Internal error: Item {} not found", elem.id));
|
|
unsafe { item.item_from_component(component.as_ptr()).as_ref().layouting_info(window) }
|
|
}
|
|
}
|