WIP
This commit is contained in:
parent
dc8e7acca0
commit
4d66c3237d
3 changed files with 354 additions and 364 deletions
|
@ -69,6 +69,181 @@ impl<V: View> Default for Node<V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<V: View> Element<V> for Node<V> {
|
||||||
|
type LayoutState = NodeLayout;
|
||||||
|
type PaintState = ();
|
||||||
|
|
||||||
|
fn layout(
|
||||||
|
&mut self,
|
||||||
|
constraint: SizeConstraint,
|
||||||
|
view: &mut V,
|
||||||
|
cx: &mut LayoutContext<V>,
|
||||||
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
|
let layout = if let Some(axis) = self.style.axis.to_2d() {
|
||||||
|
self.layout_xy(axis, constraint, cx.rem_pixels(), view, cx)
|
||||||
|
} else {
|
||||||
|
todo!()
|
||||||
|
};
|
||||||
|
|
||||||
|
(layout.size.max(constraint.min), layout)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paint(
|
||||||
|
&mut self,
|
||||||
|
scene: &mut SceneBuilder,
|
||||||
|
bounds: RectF,
|
||||||
|
visible_bounds: RectF,
|
||||||
|
layout: &mut NodeLayout,
|
||||||
|
view: &mut V,
|
||||||
|
cx: &mut PaintContext<V>,
|
||||||
|
) -> Self::PaintState {
|
||||||
|
let margined_bounds = RectF::from_points(
|
||||||
|
bounds.origin() + vec2f(layout.margins.left, layout.margins.top),
|
||||||
|
bounds.lower_right() - vec2f(layout.margins.right, layout.margins.bottom),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Paint drop shadow
|
||||||
|
for shadow in &self.style.shadows {
|
||||||
|
scene.push_shadow(scene::Shadow {
|
||||||
|
bounds: margined_bounds + shadow.offset,
|
||||||
|
corner_radius: self.style.corner_radius,
|
||||||
|
sigma: shadow.blur,
|
||||||
|
color: shadow.color,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// // Paint cursor style
|
||||||
|
// if let Some(hit_bounds) = content_bounds.intersection(visible_bounds) {
|
||||||
|
// if let Some(style) = self.style.cursor {
|
||||||
|
// scene.push_cursor_region(CursorRegion {
|
||||||
|
// bounds: hit_bounds,
|
||||||
|
// style,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Render the background and/or the border.
|
||||||
|
let Fill::Color(fill_color) = self.style.fill;
|
||||||
|
let is_fill_visible = !fill_color.is_fully_transparent();
|
||||||
|
if is_fill_visible || self.style.borders.is_visible() {
|
||||||
|
eprintln!(
|
||||||
|
"{}: paint background: {:?}",
|
||||||
|
self.id.as_deref().unwrap_or(""),
|
||||||
|
margined_bounds
|
||||||
|
);
|
||||||
|
|
||||||
|
scene.push_quad(Quad {
|
||||||
|
bounds: margined_bounds,
|
||||||
|
background: is_fill_visible.then_some(fill_color),
|
||||||
|
border: scene::Border {
|
||||||
|
width: self.style.borders.width,
|
||||||
|
color: self.style.borders.color,
|
||||||
|
overlay: false,
|
||||||
|
top: self.style.borders.top,
|
||||||
|
right: self.style.borders.right,
|
||||||
|
bottom: self.style.borders.bottom,
|
||||||
|
left: self.style.borders.left,
|
||||||
|
},
|
||||||
|
corner_radius: self.style.corner_radius,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.children.is_empty() {
|
||||||
|
// Account for padding first.
|
||||||
|
let borders = &self.style.borders;
|
||||||
|
let padded_bounds = RectF::from_points(
|
||||||
|
margined_bounds.origin()
|
||||||
|
+ vec2f(
|
||||||
|
borders.left_width() + layout.padding.left,
|
||||||
|
borders.top_width() + layout.padding.top,
|
||||||
|
),
|
||||||
|
margined_bounds.lower_right()
|
||||||
|
- vec2f(
|
||||||
|
layout.padding.right + borders.right_width(),
|
||||||
|
layout.padding.bottom + borders.bottom_width(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(axis) = self.style.axis.to_2d() {
|
||||||
|
// let parent_size = padded_bounds.size();
|
||||||
|
let mut child_origin = padded_bounds.origin();
|
||||||
|
|
||||||
|
// Align all children together along the primary axis
|
||||||
|
// let mut align_horizontally = false;
|
||||||
|
// let mut align_vertically = false;
|
||||||
|
// match axis {
|
||||||
|
// Axis2d::X => align_horizontally = true,
|
||||||
|
// Axis2d::Y => align_vertically = true,
|
||||||
|
// }
|
||||||
|
// align_child(
|
||||||
|
// &mut child_origin,
|
||||||
|
// parent_size,
|
||||||
|
// layout.content_size,
|
||||||
|
// self.style.align.0,
|
||||||
|
// align_horizontally,
|
||||||
|
// align_vertically,
|
||||||
|
// );
|
||||||
|
|
||||||
|
for child in &mut self.children {
|
||||||
|
// Align each child along the cross axis
|
||||||
|
// align_horizontally = !align_horizontally;
|
||||||
|
// align_vertically = !align_vertically;
|
||||||
|
// align_child(
|
||||||
|
// &mut child_origin,
|
||||||
|
// parent_size,
|
||||||
|
// child.size(),
|
||||||
|
// self.style.align.0,
|
||||||
|
// align_horizontally,
|
||||||
|
// align_vertically,
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
child.paint(scene, child_origin, visible_bounds, view, cx);
|
||||||
|
|
||||||
|
// Advance along the primary axis by the size of this child
|
||||||
|
child_origin.set(axis, child_origin.get(axis) + child.size().get(axis));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rect_for_text_range(
|
||||||
|
&self,
|
||||||
|
range_utf16: Range<usize>,
|
||||||
|
_: RectF,
|
||||||
|
_: RectF,
|
||||||
|
_: &Self::LayoutState,
|
||||||
|
_: &Self::PaintState,
|
||||||
|
view: &V,
|
||||||
|
cx: &ViewContext<V>,
|
||||||
|
) -> Option<RectF> {
|
||||||
|
self.children
|
||||||
|
.iter()
|
||||||
|
.find_map(|child| child.rect_for_text_range(range_utf16.clone(), view, cx))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn debug(
|
||||||
|
&self,
|
||||||
|
bounds: RectF,
|
||||||
|
_: &Self::LayoutState,
|
||||||
|
_: &Self::PaintState,
|
||||||
|
view: &V,
|
||||||
|
cx: &ViewContext<V>,
|
||||||
|
) -> Value {
|
||||||
|
json!({
|
||||||
|
"type": "Node",
|
||||||
|
"bounds": bounds.to_json(),
|
||||||
|
// TODO!
|
||||||
|
// "children": self.content.iter().map(|child| child.debug(view, cx)).collect::<Vec<Value>>()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn metadata(&self) -> Option<&dyn Any> {
|
||||||
|
Some(&self.style)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<V: View> Node<V> {
|
impl<V: View> Node<V> {
|
||||||
pub fn id(mut self, id: impl Into<Cow<'static, str>>) -> Self {
|
pub fn id(mut self, id: impl Into<Cow<'static, str>>) -> Self {
|
||||||
self.id = Some(id.into());
|
self.id = Some(id.into());
|
||||||
|
@ -146,12 +321,12 @@ impl<V: View> Node<V> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn id_as_str(&self) -> &str {
|
fn id_as_string(&self) -> String {
|
||||||
self.id.as_deref().unwrap_or("<anonymous>")
|
self.id.as_deref().unwrap_or("<anonymous>").to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_children(&mut self, constraint: SizeConstraint) -> Vector2F {
|
fn log(&self, s: &str) {
|
||||||
todo!()
|
eprintln!("{}: {}", self.id_as_string(), s);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_xy(
|
fn layout_xy(
|
||||||
|
@ -162,7 +337,10 @@ impl<V: View> Node<V> {
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut LayoutContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> NodeLayout {
|
) -> NodeLayout {
|
||||||
let mut child_constraint = SizeConstraint::default();
|
self.log(&format!("{:?}", constraint));
|
||||||
|
|
||||||
|
let cross_axis = primary_axis.rotate();
|
||||||
|
let total_flex = self.style.flex();
|
||||||
let mut layout = NodeLayout {
|
let mut layout = NodeLayout {
|
||||||
size: Default::default(),
|
size: Default::default(),
|
||||||
padding: self.style.padding.fixed_pixels(rem_pixels),
|
padding: self.style.padding.fixed_pixels(rem_pixels),
|
||||||
|
@ -172,51 +350,42 @@ impl<V: View> Node<V> {
|
||||||
let fixed_padding_size = layout.padding.size();
|
let fixed_padding_size = layout.padding.size();
|
||||||
let fixed_margin_size = layout.margins.size();
|
let fixed_margin_size = layout.margins.size();
|
||||||
let borders_size = layout.borders.size();
|
let borders_size = layout.borders.size();
|
||||||
let flex_size = dbg!(self.style.flex());
|
let padded_constraint = constraint - fixed_margin_size - borders_size - fixed_padding_size;
|
||||||
let cross_axis = primary_axis.rotate();
|
let mut child_constraint = SizeConstraint::default();
|
||||||
|
|
||||||
|
dbg!(self.id_as_string());
|
||||||
for axis in [Axis2d::X, Axis2d::Y] {
|
for axis in [Axis2d::X, Axis2d::Y] {
|
||||||
|
dbg!(axis);
|
||||||
let length = self.style.size.get(axis);
|
let length = self.style.size.get(axis);
|
||||||
|
|
||||||
// Before we layout children
|
|
||||||
match length {
|
match length {
|
||||||
Length::Fixed(fixed_length) => {
|
Length::Fixed(fixed_length) => {
|
||||||
|
// If the length is fixed, we calculate flexible padding and margins
|
||||||
|
// before laying out the children.
|
||||||
let fixed_length = fixed_length.to_pixels(rem_pixels);
|
let fixed_length = fixed_length.to_pixels(rem_pixels);
|
||||||
let mut remaining_length = constraint.min.get(axis)
|
let mut remaining_flex = total_flex.get(axis);
|
||||||
- fixed_margin_size.get(axis)
|
let mut remaining_length = padded_constraint.max.get(axis) - fixed_length;
|
||||||
- borders_size.get(axis)
|
|
||||||
- fixed_padding_size.get(axis)
|
|
||||||
- fixed_length;
|
|
||||||
|
|
||||||
let mut remaining_flex = flex_size.get(axis);
|
// Here we avoid the padding exceeding the fixed length by giving
|
||||||
|
// the padding calculation its own remaining_flex and remaining_length.
|
||||||
// Distribute remaining length to flexible padding, but only so long as the
|
|
||||||
// padding does not exceed the fixed length.
|
|
||||||
let mut padding_flex = self.style.padding.flex().get(axis);
|
let mut padding_flex = self.style.padding.flex().get(axis);
|
||||||
let mut padding_length =
|
let mut padding_length =
|
||||||
((padding_flex / remaining_flex) * remaining_length).min(fixed_length);
|
((padding_flex / remaining_flex) * remaining_length).min(fixed_length);
|
||||||
|
layout.padding.compute_flex_edges(
|
||||||
|
&self.style.padding,
|
||||||
|
axis,
|
||||||
|
&mut padding_flex,
|
||||||
|
&mut padding_length,
|
||||||
|
rem_pixels,
|
||||||
|
);
|
||||||
remaining_flex -= padding_flex;
|
remaining_flex -= padding_flex;
|
||||||
*layout.padding.start_mut(axis) += self.style.padding.start(axis).flex_pixels(
|
remaining_length -= padding_flex;
|
||||||
rem_pixels,
|
layout.margins.compute_flex_edges(
|
||||||
&mut padding_flex,
|
&self.style.margins,
|
||||||
&mut padding_length,
|
axis,
|
||||||
);
|
|
||||||
*layout.padding.end_mut(axis) += self.style.padding.end(axis).flex_pixels(
|
|
||||||
rem_pixels,
|
|
||||||
&mut padding_flex,
|
|
||||||
&mut padding_length,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Distribute remaining length to flexible margins.
|
|
||||||
*layout.margins.start_mut(axis) += self.style.margins.start(axis).flex_pixels(
|
|
||||||
rem_pixels,
|
|
||||||
&mut remaining_flex,
|
&mut remaining_flex,
|
||||||
&mut remaining_length,
|
&mut remaining_length,
|
||||||
);
|
|
||||||
*layout.margins.end_mut(axis) += self.style.margins.end(axis).flex_pixels(
|
|
||||||
rem_pixels,
|
rem_pixels,
|
||||||
&mut remaining_flex,
|
|
||||||
&mut remaining_length,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
child_constraint.max.set(axis, remaining_length);
|
child_constraint.max.set(axis, remaining_length);
|
||||||
|
@ -228,17 +397,14 @@ impl<V: View> Node<V> {
|
||||||
// If the length is flex, we calculate the content's share first.
|
// If the length is flex, we calculate the content's share first.
|
||||||
// We then layout the children and determine the flexible padding
|
// We then layout the children and determine the flexible padding
|
||||||
// and margins in a second phase.
|
// and margins in a second phase.
|
||||||
let mut remaining_flex = flex_size.get(axis);
|
let mut remaining_flex = total_flex.get(axis);
|
||||||
let mut remaining_length = constraint.max.get(axis)
|
let mut remaining_length = dbg!(padded_constraint.max.get(axis));
|
||||||
- fixed_margin_size.get(axis)
|
let content_length =
|
||||||
- borders_size.get(axis)
|
|
||||||
- fixed_padding_size.get(axis);
|
|
||||||
|
|
||||||
let children_length =
|
|
||||||
length.flex_pixels(rem_pixels, &mut remaining_flex, &mut remaining_length);
|
length.flex_pixels(rem_pixels, &mut remaining_flex, &mut remaining_length);
|
||||||
child_constraint.max.set(axis, children_length);
|
dbg!(content_length);
|
||||||
|
child_constraint.max.set(axis, content_length);
|
||||||
if axis == cross_axis {
|
if axis == cross_axis {
|
||||||
child_constraint.min.set(axis, children_length);
|
child_constraint.min.set(axis, content_length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Length::Hug => {
|
Length::Hug => {
|
||||||
|
@ -249,62 +415,71 @@ impl<V: View> Node<V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Layout fixed children using the child constraint determined above.
|
let content_size = {
|
||||||
let mut remaining_child_length = child_constraint.max.get(primary_axis);
|
dbg!(self.id_as_string(), "lay out children");
|
||||||
let mut remaining_child_flex = 0.;
|
// Layout fixed children using the child constraint determined above.
|
||||||
let mut total_child_length = 0.;
|
let mut remaining_child_length = dbg!(child_constraint.max).get(primary_axis);
|
||||||
let mut cross_axis_max: f32 = 0.;
|
let mut remaining_child_flex = 0.;
|
||||||
child_constraint.min.set(primary_axis, 0.);
|
let mut total_child_length = 0.;
|
||||||
child_constraint.max.set(primary_axis, 0.);
|
let mut cross_axis_max: f32 = 0.;
|
||||||
|
child_constraint.min.set(primary_axis, 0.);
|
||||||
|
child_constraint.max.set(primary_axis, 0.);
|
||||||
|
|
||||||
for child in &mut self.children {
|
for child in &mut self.children {
|
||||||
// Skip children that are flexible in the primary for this first pass.
|
// Don't lay out children that are flexible along the primary for this first pass,
|
||||||
if let Some(child_flex) = child
|
// but total up their flex for use in the second pass.
|
||||||
.metadata::<NodeStyle>()
|
if let Some(child_flex) = child
|
||||||
.map(|style| style.flex().get(primary_axis))
|
.metadata::<NodeStyle>()
|
||||||
{
|
.map(|style| style.flex().get(primary_axis))
|
||||||
if child_flex > 0. {
|
{
|
||||||
remaining_child_flex += child_flex;
|
if child_flex > 0. {
|
||||||
continue;
|
remaining_child_flex += child_flex;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The child is fixed along the primary axis, so perform layout.
|
||||||
|
let child_size = child.layout(child_constraint, view, cx);
|
||||||
|
let child_length = child_size.get(primary_axis);
|
||||||
|
remaining_child_length -= child_length;
|
||||||
|
total_child_length += child_length;
|
||||||
|
cross_axis_max = cross_axis_max.max(child_size.get(cross_axis));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now divide the remaining length among the flexible children.
|
||||||
|
let id = self.id_as_string();
|
||||||
|
for child in &mut self.children {
|
||||||
|
if let Some(child_flex) = child
|
||||||
|
.metadata::<NodeStyle>()
|
||||||
|
.map(|style| style.flex().get(primary_axis))
|
||||||
|
{
|
||||||
|
if child_flex > 0. {
|
||||||
|
eprintln!("{}: child is flexible", id);
|
||||||
|
|
||||||
|
let max_child_length =
|
||||||
|
(child_flex / remaining_child_flex) * remaining_child_length;
|
||||||
|
child_constraint.max.set(primary_axis, max_child_length);
|
||||||
|
|
||||||
|
let child_size = child.layout(child_constraint, view, cx);
|
||||||
|
let child_length = child_size.get(primary_axis);
|
||||||
|
total_child_length += child_length;
|
||||||
|
remaining_child_length -= child_length;
|
||||||
|
remaining_child_flex -= child_flex;
|
||||||
|
cross_axis_max = cross_axis_max.max(child_size.get(cross_axis));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The child is fixed along the primary axis, so perform layout.
|
match primary_axis {
|
||||||
let child_size = child.layout(child_constraint, view, cx);
|
Axis2d::X => vec2f(total_child_length, cross_axis_max),
|
||||||
let child_length = child_size.get(primary_axis);
|
Axis2d::Y => vec2f(cross_axis_max, total_child_length),
|
||||||
remaining_child_length -= child_length;
|
|
||||||
total_child_length += child_length;
|
|
||||||
cross_axis_max = cross_axis_max.max(child_size.get(cross_axis));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now layout all the flexible children.
|
|
||||||
for child in &mut self.children {
|
|
||||||
if let Some(child_flex) = child
|
|
||||||
.metadata::<NodeStyle>()
|
|
||||||
.map(|style| style.flex().get(primary_axis))
|
|
||||||
{
|
|
||||||
if child_flex > 0. {
|
|
||||||
child_constraint.max.set(
|
|
||||||
primary_axis,
|
|
||||||
(child_flex / remaining_child_flex) * remaining_child_length,
|
|
||||||
);
|
|
||||||
|
|
||||||
let child_size = child.layout(child_constraint, view, cx);
|
|
||||||
let child_length = child_size.get(primary_axis);
|
|
||||||
total_child_length += child_length;
|
|
||||||
remaining_child_length -= child_length;
|
|
||||||
remaining_child_flex -= child_flex;
|
|
||||||
cross_axis_max = cross_axis_max.max(child_size.get(cross_axis));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let content_size = match primary_axis {
|
|
||||||
Axis2d::X => vec2f(total_child_length, cross_axis_max),
|
|
||||||
Axis2d::Y => vec2f(cross_axis_max, total_child_length),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Now distribute remaining space to flexible padding and margins.
|
||||||
|
dbg!(self.id_as_string());
|
||||||
for axis in [Axis2d::X, Axis2d::Y] {
|
for axis in [Axis2d::X, Axis2d::Y] {
|
||||||
|
dbg!(axis);
|
||||||
let length = self.style.size.get(axis);
|
let length = self.style.size.get(axis);
|
||||||
|
|
||||||
// Finish with flexible margins and padding now that children are laid out.
|
// Finish with flexible margins and padding now that children are laid out.
|
||||||
|
@ -312,37 +487,23 @@ impl<V: View> Node<V> {
|
||||||
Length::Hug => {
|
Length::Hug => {
|
||||||
// Now that we know the size of our children, we can distribute
|
// Now that we know the size of our children, we can distribute
|
||||||
// space to flexible padding and margins.
|
// space to flexible padding and margins.
|
||||||
let mut remaining_flex = flex_size.get(axis);
|
let mut remaining_flex = total_flex.get(axis);
|
||||||
let mut remaining_length = constraint.max.get(axis)
|
let mut remaining_length =
|
||||||
- fixed_margin_size.get(axis)
|
padded_constraint.min.get(axis) - content_size.get(axis);
|
||||||
- borders_size.get(axis)
|
layout.padding.compute_flex_edges(
|
||||||
- fixed_padding_size.get(axis)
|
&self.style.padding,
|
||||||
- content_size.get(axis);
|
axis,
|
||||||
|
|
||||||
// Distribute remaining length to flexible padding
|
|
||||||
*layout.padding.start_mut(axis) += self.style.padding.start(axis).flex_pixels(
|
|
||||||
rem_pixels,
|
|
||||||
&mut remaining_flex,
|
&mut remaining_flex,
|
||||||
&mut remaining_length,
|
&mut remaining_length,
|
||||||
);
|
|
||||||
*layout.padding.end_mut(axis) += self.style.padding.end(axis).flex_pixels(
|
|
||||||
rem_pixels,
|
rem_pixels,
|
||||||
|
);
|
||||||
|
layout.margins.compute_flex_edges(
|
||||||
|
&self.style.margins,
|
||||||
|
axis,
|
||||||
&mut remaining_flex,
|
&mut remaining_flex,
|
||||||
&mut remaining_length,
|
&mut remaining_length,
|
||||||
);
|
|
||||||
|
|
||||||
// Distribute remaining length to flexible margins.
|
|
||||||
*layout.margins.start_mut(axis) += self.style.margins.start(axis).flex_pixels(
|
|
||||||
rem_pixels,
|
rem_pixels,
|
||||||
&mut remaining_flex,
|
|
||||||
&mut remaining_length,
|
|
||||||
);
|
);
|
||||||
*layout.margins.end_mut(axis) += self.style.margins.end(axis).flex_pixels(
|
|
||||||
rem_pixels,
|
|
||||||
&mut remaining_flex,
|
|
||||||
&mut remaining_length,
|
|
||||||
);
|
|
||||||
|
|
||||||
layout.size.set(
|
layout.size.set(
|
||||||
axis,
|
axis,
|
||||||
content_size.get(axis)
|
content_size.get(axis)
|
||||||
|
@ -353,249 +514,59 @@ impl<V: View> Node<V> {
|
||||||
}
|
}
|
||||||
Length::Fixed(fixed) => {
|
Length::Fixed(fixed) => {
|
||||||
// For a fixed length, we've already computed margins and padding
|
// For a fixed length, we've already computed margins and padding
|
||||||
// before laying out children.
|
// before laying out children. Padding and border are included in the
|
||||||
|
// fixed length, so we just add the margins to determine the size.
|
||||||
layout.size.set(
|
layout.size.set(
|
||||||
axis,
|
axis,
|
||||||
fixed.to_pixels(rem_pixels) + layout.margins.size().get(axis),
|
fixed.to_pixels(rem_pixels) + layout.margins.size().get(axis),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Length::Auto { .. } => {
|
Length::Auto { .. } => {
|
||||||
// If the length is flex, we subtract the fixed margins, padding, and
|
let mut remaining_flex = total_flex.get(axis);
|
||||||
// children length along the current dimension, then distribute the
|
let mut remaining_length = padded_constraint.max.get(axis);
|
||||||
// remaining length among margins and padding.
|
|
||||||
let mut remaining_flex = flex_size.get(axis);
|
|
||||||
let mut remaining_length = constraint.max.get(axis)
|
|
||||||
- fixed_margin_size.get(axis)
|
|
||||||
- borders_size.get(axis)
|
|
||||||
- fixed_padding_size.get(axis);
|
|
||||||
|
|
||||||
// Performed for its side effect of decrementing the remaining
|
dbg!(remaining_flex, remaining_length);
|
||||||
// flex and length.
|
|
||||||
self.style.size.get(axis).flex_pixels(
|
let flex_length =
|
||||||
rem_pixels,
|
length.flex_pixels(rem_pixels, &mut remaining_flex, &mut remaining_length);
|
||||||
|
|
||||||
|
dbg!(flex_length, remaining_flex, remaining_length);
|
||||||
|
|
||||||
|
layout.padding.compute_flex_edges(
|
||||||
|
&self.style.padding,
|
||||||
|
axis,
|
||||||
&mut remaining_flex,
|
&mut remaining_flex,
|
||||||
&mut remaining_length,
|
&mut remaining_length,
|
||||||
|
rem_pixels,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Distribute remaining length to flexible padding
|
dbg!(remaining_flex, remaining_length);
|
||||||
*layout.padding.start_mut(axis) += self.style.padding.start(axis).flex_pixels(
|
|
||||||
rem_pixels,
|
layout.margins.compute_flex_edges(
|
||||||
|
&self.style.margins,
|
||||||
|
axis,
|
||||||
&mut remaining_flex,
|
&mut remaining_flex,
|
||||||
&mut remaining_length,
|
&mut remaining_length,
|
||||||
);
|
|
||||||
*layout.padding.end_mut(axis) += self.style.padding.end(axis).flex_pixels(
|
|
||||||
rem_pixels,
|
rem_pixels,
|
||||||
&mut remaining_flex,
|
|
||||||
&mut remaining_length,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Distribute remaining length to flexible margins.
|
dbg!(remaining_flex, remaining_length);
|
||||||
*layout.margins.start_mut(axis) += self.style.margins.start(axis).flex_pixels(
|
|
||||||
rem_pixels,
|
layout.size.set(
|
||||||
&mut remaining_flex,
|
axis,
|
||||||
&mut remaining_length,
|
flex_length
|
||||||
);
|
+ layout.padding.size().get(axis)
|
||||||
*layout.margins.end_mut(axis) += self.style.margins.end(axis).flex_pixels(
|
+ layout.borders.size().get(axis)
|
||||||
rem_pixels,
|
+ layout.margins.size().get(axis),
|
||||||
&mut remaining_flex,
|
)
|
||||||
&mut remaining_length,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.log(&format!("{:?}", layout));
|
||||||
|
|
||||||
layout
|
layout
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint_children_xy(
|
|
||||||
&mut self,
|
|
||||||
scene: &mut SceneBuilder,
|
|
||||||
axis: Axis2d,
|
|
||||||
bounds: RectF,
|
|
||||||
visible_bounds: RectF,
|
|
||||||
layout: &mut NodeLayout,
|
|
||||||
view: &mut V,
|
|
||||||
cx: &mut ViewContext<V>,
|
|
||||||
) {
|
|
||||||
let parent_size = bounds.size();
|
|
||||||
let mut child_origin = bounds.origin();
|
|
||||||
|
|
||||||
// Align all children together along the primary axis
|
|
||||||
// let mut align_horizontally = false;
|
|
||||||
// let mut align_vertically = false;
|
|
||||||
// match axis {
|
|
||||||
// Axis2d::X => align_horizontally = true,
|
|
||||||
// Axis2d::Y => align_vertically = true,
|
|
||||||
// }
|
|
||||||
// align_child(
|
|
||||||
// &mut child_origin,
|
|
||||||
// parent_size,
|
|
||||||
// layout.content_size,
|
|
||||||
// self.style.align.0,
|
|
||||||
// align_horizontally,
|
|
||||||
// align_vertically,
|
|
||||||
// );
|
|
||||||
|
|
||||||
for child in &mut self.children {
|
|
||||||
// Align each child along the cross axis
|
|
||||||
// align_horizontally = !align_horizontally;
|
|
||||||
// align_vertically = !align_vertically;
|
|
||||||
// align_child(
|
|
||||||
// &mut child_origin,
|
|
||||||
// parent_size,
|
|
||||||
// child.size(),
|
|
||||||
// self.style.align.0,
|
|
||||||
// align_horizontally,
|
|
||||||
// align_vertically,
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
child.paint(scene, child_origin, visible_bounds, view, cx);
|
|
||||||
|
|
||||||
// Advance along the primary axis by the size of this child
|
|
||||||
child_origin.set(axis, child_origin.get(axis) + child.size().get(axis));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: View> Element<V> for Node<V> {
|
|
||||||
type LayoutState = NodeLayout;
|
|
||||||
type PaintState = ();
|
|
||||||
|
|
||||||
fn layout(
|
|
||||||
&mut self,
|
|
||||||
constraint: SizeConstraint,
|
|
||||||
view: &mut V,
|
|
||||||
cx: &mut LayoutContext<V>,
|
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
|
||||||
let layout = if let Some(axis) = self.style.axis.to_2d() {
|
|
||||||
self.layout_xy(axis, constraint, cx.rem_pixels(), view, cx)
|
|
||||||
} else {
|
|
||||||
todo!()
|
|
||||||
};
|
|
||||||
|
|
||||||
(layout.size.max(constraint.min), layout)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paint(
|
|
||||||
&mut self,
|
|
||||||
scene: &mut SceneBuilder,
|
|
||||||
bounds: RectF,
|
|
||||||
visible_bounds: RectF,
|
|
||||||
layout: &mut NodeLayout,
|
|
||||||
view: &mut V,
|
|
||||||
cx: &mut PaintContext<V>,
|
|
||||||
) -> Self::PaintState {
|
|
||||||
dbg!(&layout);
|
|
||||||
|
|
||||||
let margined_bounds = RectF::from_points(
|
|
||||||
bounds.origin() + vec2f(layout.margins.left, layout.margins.top),
|
|
||||||
bounds.lower_right() - vec2f(layout.margins.right, layout.margins.bottom),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Paint drop shadow
|
|
||||||
for shadow in &self.style.shadows {
|
|
||||||
scene.push_shadow(scene::Shadow {
|
|
||||||
bounds: margined_bounds + shadow.offset,
|
|
||||||
corner_radius: self.style.corner_radius,
|
|
||||||
sigma: shadow.blur,
|
|
||||||
color: shadow.color,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// // Paint cursor style
|
|
||||||
// if let Some(hit_bounds) = content_bounds.intersection(visible_bounds) {
|
|
||||||
// if let Some(style) = self.style.cursor {
|
|
||||||
// scene.push_cursor_region(CursorRegion {
|
|
||||||
// bounds: hit_bounds,
|
|
||||||
// style,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Render the background and/or the border.
|
|
||||||
let Fill::Color(fill_color) = self.style.fill;
|
|
||||||
let is_fill_visible = !fill_color.is_fully_transparent();
|
|
||||||
if is_fill_visible || self.style.borders.is_visible() {
|
|
||||||
eprintln!(
|
|
||||||
"{}: paint background: {:?}",
|
|
||||||
self.id.as_deref().unwrap_or(""),
|
|
||||||
margined_bounds
|
|
||||||
);
|
|
||||||
|
|
||||||
scene.push_quad(Quad {
|
|
||||||
bounds: margined_bounds,
|
|
||||||
background: is_fill_visible.then_some(fill_color),
|
|
||||||
border: scene::Border {
|
|
||||||
width: self.style.borders.width,
|
|
||||||
color: self.style.borders.color,
|
|
||||||
overlay: false,
|
|
||||||
top: self.style.borders.top,
|
|
||||||
right: self.style.borders.right,
|
|
||||||
bottom: self.style.borders.bottom,
|
|
||||||
left: self.style.borders.left,
|
|
||||||
},
|
|
||||||
corner_radius: self.style.corner_radius,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.children.is_empty() {
|
|
||||||
// Account for padding first.
|
|
||||||
let borders = &self.style.borders;
|
|
||||||
let padded_bounds = RectF::from_points(
|
|
||||||
margined_bounds.origin()
|
|
||||||
+ vec2f(
|
|
||||||
borders.left_width() + layout.padding.left,
|
|
||||||
borders.top_width() + layout.padding.top,
|
|
||||||
),
|
|
||||||
margined_bounds.lower_right()
|
|
||||||
- vec2f(
|
|
||||||
layout.padding.right + borders.right_width(),
|
|
||||||
layout.padding.bottom + borders.bottom_width(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(axis) = self.style.axis.to_2d() {
|
|
||||||
self.paint_children_xy(scene, axis, padded_bounds, visible_bounds, layout, view, cx)
|
|
||||||
} else {
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rect_for_text_range(
|
|
||||||
&self,
|
|
||||||
range_utf16: Range<usize>,
|
|
||||||
_: RectF,
|
|
||||||
_: RectF,
|
|
||||||
_: &Self::LayoutState,
|
|
||||||
_: &Self::PaintState,
|
|
||||||
view: &V,
|
|
||||||
cx: &ViewContext<V>,
|
|
||||||
) -> Option<RectF> {
|
|
||||||
self.children
|
|
||||||
.iter()
|
|
||||||
.find_map(|child| child.rect_for_text_range(range_utf16.clone(), view, cx))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn debug(
|
|
||||||
&self,
|
|
||||||
bounds: RectF,
|
|
||||||
_: &Self::LayoutState,
|
|
||||||
_: &Self::PaintState,
|
|
||||||
view: &V,
|
|
||||||
cx: &ViewContext<V>,
|
|
||||||
) -> Value {
|
|
||||||
json!({
|
|
||||||
"type": "Node",
|
|
||||||
"bounds": bounds.to_json(),
|
|
||||||
// TODO!
|
|
||||||
// "children": self.content.iter().map(|child| child.debug(view, cx)).collect::<Vec<Value>>()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn metadata(&self) -> Option<&dyn Any> {
|
|
||||||
Some(&self.style)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TopBottom {
|
pub struct TopBottom {
|
||||||
|
@ -793,6 +764,24 @@ impl Edges<f32> {
|
||||||
fn size(&self) -> Vector2F {
|
fn size(&self) -> Vector2F {
|
||||||
vec2f(self.left + self.right, self.top + self.bottom)
|
vec2f(self.left + self.right, self.top + self.bottom)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compute_flex_edges(
|
||||||
|
&mut self,
|
||||||
|
style_edges: &Edges<Length>,
|
||||||
|
axis: Axis2d,
|
||||||
|
remaining_flex: &mut f32,
|
||||||
|
remaining_length: &mut f32,
|
||||||
|
rem_pixels: f32,
|
||||||
|
) {
|
||||||
|
*self.start_mut(axis) +=
|
||||||
|
style_edges
|
||||||
|
.start(axis)
|
||||||
|
.flex_pixels(rem_pixels, remaining_flex, remaining_length);
|
||||||
|
*self.end_mut(axis) +=
|
||||||
|
style_edges
|
||||||
|
.end(axis)
|
||||||
|
.flex_pixels(rem_pixels, remaining_flex, remaining_length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Edges<Length> {
|
impl Edges<Length> {
|
||||||
|
@ -1080,7 +1069,7 @@ impl Axis3d {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default, PartialEq, Eq)]
|
#[derive(Clone, Copy, Default, PartialEq, Eq, Debug)]
|
||||||
pub enum Axis2d {
|
pub enum Axis2d {
|
||||||
X,
|
X,
|
||||||
#[default]
|
#[default]
|
||||||
|
|
|
@ -18,34 +18,24 @@ impl<V: View> Playground<V> {
|
||||||
.id("red column")
|
.id("red column")
|
||||||
.width(auto())
|
.width(auto())
|
||||||
.height(auto())
|
.height(auto())
|
||||||
.margin_bottom(auto())
|
|
||||||
.fill(Color::red())
|
.fill(Color::red())
|
||||||
// .child(
|
.child(
|
||||||
// row()
|
row()
|
||||||
// .id("green row")
|
.id("green row")
|
||||||
// .width(auto())
|
.width(auto())
|
||||||
// .height(rems(20.))
|
.height(rems(20.))
|
||||||
// .fill(Color::green()),
|
.margins(rems(0.), auto())
|
||||||
// )
|
.fill(Color::green())
|
||||||
// .fill(Color::green()), // .child(
|
.child(
|
||||||
// // row()
|
row()
|
||||||
// // .id("blue child")
|
.id("blue row")
|
||||||
// // .height(auto())
|
.width(rems(20.))
|
||||||
// // .width(rems(20.))
|
.height(auto())
|
||||||
// // .fill(Color::blue())
|
.fill(Color::blue()),
|
||||||
// // .margin_left(auto()),
|
),
|
||||||
// // ),
|
)
|
||||||
// )
|
|
||||||
.into_any()
|
.into_any()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn action_1(_: &mut V, data: &usize, _: &mut ViewContext<V>) {
|
|
||||||
println!("action 1: data is {}", *data);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn action_2(_: &mut V, data: &usize, _: &mut ViewContext<V>) {
|
|
||||||
println!("action 1: data is {}", *data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait DialogDelegate<V: View>: 'static {}
|
pub trait DialogDelegate<V: View>: 'static {}
|
||||||
|
|
|
@ -30,7 +30,7 @@ use sqlez::{
|
||||||
use std::{
|
use std::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
mem,
|
mem,
|
||||||
ops::{Deref, DerefMut, Range},
|
ops::{Deref, DerefMut, Range, Sub},
|
||||||
};
|
};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
@ -1332,6 +1332,17 @@ impl SizeConstraint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Sub<Vector2F> for SizeConstraint {
|
||||||
|
type Output = SizeConstraint;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Vector2F) -> SizeConstraint {
|
||||||
|
SizeConstraint {
|
||||||
|
min: self.min - rhs,
|
||||||
|
max: self.max - rhs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for SizeConstraint {
|
impl Default for SizeConstraint {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
SizeConstraint {
|
SizeConstraint {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue