Add a Flexible element that works like in Flutter

This commit is contained in:
Antonio Scandurra 2021-09-03 11:40:18 +02:00
parent ec36d818c0
commit a0dd41cdf6
4 changed files with 143 additions and 35 deletions

View File

@ -32,8 +32,46 @@ impl Flex {
Self::new(Axis::Vertical)
}
fn child_flex<'b>(child: &ElementBox) -> Option<f32> {
child.metadata::<FlexParentData>().map(|data| data.flex)
fn layout_flex_children(
&mut self,
expanded: bool,
constraint: SizeConstraint,
remaining_space: &mut f32,
remaining_flex: &mut f32,
cross_axis_max: &mut f32,
cx: &mut LayoutContext,
) {
let cross_axis = self.axis.invert();
for child in &mut self.children {
if let Some(metadata) = child.metadata::<FlexParentData>() {
if metadata.expanded != expanded {
continue;
}
let flex = metadata.flex;
let child_max = if *remaining_flex == 0.0 {
*remaining_space
} else {
let space_per_flex = *remaining_space / *remaining_flex;
space_per_flex * flex
};
let child_min = if expanded { child_max } else { 0. };
let child_constraint = match self.axis {
Axis::Horizontal => SizeConstraint::new(
vec2f(child_min, constraint.min.y()),
vec2f(child_max, constraint.max.y()),
),
Axis::Vertical => SizeConstraint::new(
vec2f(constraint.min.x(), child_min),
vec2f(constraint.max.x(), child_max),
),
};
let child_size = child.layout(child_constraint, cx);
*remaining_space -= child_size.along(self.axis);
*remaining_flex -= flex;
*cross_axis_max = cross_axis_max.max(child_size.along(cross_axis));
}
}
}
}
@ -58,8 +96,8 @@ impl Element for Flex {
let cross_axis = self.axis.invert();
let mut cross_axis_max: f32 = 0.0;
for child in &mut self.children {
if let Some(flex) = Self::child_flex(&child) {
total_flex += flex;
if let Some(metadata) = child.metadata::<FlexParentData>() {
total_flex += metadata.flex;
} else {
let child_constraint = match self.axis {
Axis::Horizontal => SizeConstraint::new(
@ -84,30 +122,22 @@ impl Element for Flex {
let mut remaining_space = constraint.max_along(self.axis) - fixed_space;
let mut remaining_flex = total_flex;
for child in &mut self.children {
if let Some(flex) = Self::child_flex(&child) {
let child_max = if remaining_flex == 0.0 {
remaining_space
} else {
let space_per_flex = remaining_space / remaining_flex;
space_per_flex * flex
};
let child_constraint = match self.axis {
Axis::Horizontal => SizeConstraint::new(
vec2f(0.0, constraint.min.y()),
vec2f(child_max, constraint.max.y()),
),
Axis::Vertical => SizeConstraint::new(
vec2f(constraint.min.x(), 0.0),
vec2f(constraint.max.x(), child_max),
),
};
let child_size = child.layout(child_constraint, cx);
remaining_space -= child_size.along(self.axis);
remaining_flex -= flex;
cross_axis_max = cross_axis_max.max(child_size.along(cross_axis));
}
}
self.layout_flex_children(
false,
constraint,
&mut remaining_space,
&mut remaining_flex,
&mut cross_axis_max,
cx,
);
self.layout_flex_children(
true,
constraint,
&mut remaining_space,
&mut remaining_flex,
&mut cross_axis_max,
cx,
);
match self.axis {
Axis::Horizontal => vec2f(constraint.max.x() - remaining_space, cross_axis_max),
@ -181,6 +211,7 @@ impl Element for Flex {
struct FlexParentData {
flex: f32,
expanded: bool,
}
pub struct Expanded {
@ -191,7 +222,10 @@ pub struct Expanded {
impl Expanded {
pub fn new(flex: f32, child: ElementBox) -> Self {
Expanded {
metadata: FlexParentData { flex },
metadata: FlexParentData {
flex,
expanded: true,
},
child,
}
}
@ -249,3 +283,73 @@ impl Element for Expanded {
})
}
}
pub struct Flexible {
metadata: FlexParentData,
child: ElementBox,
}
impl Flexible {
pub fn new(flex: f32, child: ElementBox) -> Self {
Flexible {
metadata: FlexParentData {
flex,
expanded: false,
},
child,
}
}
}
impl Element for Flexible {
type LayoutState = ();
type PaintState = ();
fn layout(
&mut self,
constraint: SizeConstraint,
cx: &mut LayoutContext,
) -> (Vector2F, Self::LayoutState) {
let size = self.child.layout(constraint, cx);
(size, ())
}
fn paint(
&mut self,
bounds: RectF,
visible_bounds: RectF,
_: &mut Self::LayoutState,
cx: &mut PaintContext,
) -> Self::PaintState {
self.child.paint(bounds.origin(), visible_bounds, cx)
}
fn dispatch_event(
&mut self,
event: &Event,
_: RectF,
_: &mut Self::LayoutState,
_: &mut Self::PaintState,
cx: &mut EventContext,
) -> bool {
self.child.dispatch_event(event, cx)
}
fn metadata(&self) -> Option<&dyn Any> {
Some(&self.metadata)
}
fn debug(
&self,
_: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
cx: &DebugContext,
) -> Value {
json!({
"type": "Flexible",
"flex": self.metadata.flex,
"child": self.child.debug(cx)
})
}
}

View File

@ -964,13 +964,13 @@ impl View for Workspace {
if let Some(element) =
self.left_sidebar.render_active_item(&settings, cx)
{
content.add_child(element);
content.add_child(Flexible::new(0.8, element).boxed());
}
content.add_child(Expanded::new(1.0, self.center.render()).boxed());
if let Some(element) =
self.right_sidebar.render_active_item(&settings, cx)
{
content.add_child(element);
content.add_child(Flexible::new(0.8, element).boxed());
}
content.add_child(self.right_sidebar.render(&settings, cx));
content.boxed()

View File

@ -191,7 +191,7 @@ impl Pane {
let border = &theme.workspace.tab.container.border;
row.add_child(
Expanded::new(
Flexible::new(
1.0,
MouseEventHandler::new::<Tab, _, _, _>(item.id(), cx, |mouse_state, cx| {
let title = item.title(cx);

View File

@ -113,9 +113,13 @@ impl Sidebar {
container.add_child(self.render_resize_handle(settings, cx));
}
container.add_child(
ConstrainedBox::new(ChildView::new(active_item.id()).boxed())
.with_width(*self.width.borrow())
.boxed(),
Flexible::new(
1.,
ConstrainedBox::new(ChildView::new(active_item.id()).boxed())
.with_max_width(*self.width.borrow())
.boxed(),
)
.boxed(),
);
if matches!(self.side, Side::Left) {
container.add_child(self.render_resize_handle(settings, cx));