Add a Flexible
element that works like in Flutter
This commit is contained in:
parent
ec36d818c0
commit
a0dd41cdf6
4 changed files with 143 additions and 35 deletions
|
@ -32,8 +32,46 @@ impl Flex {
|
||||||
Self::new(Axis::Vertical)
|
Self::new(Axis::Vertical)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn child_flex<'b>(child: &ElementBox) -> Option<f32> {
|
fn layout_flex_children(
|
||||||
child.metadata::<FlexParentData>().map(|data| data.flex)
|
&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 cross_axis = self.axis.invert();
|
||||||
let mut cross_axis_max: f32 = 0.0;
|
let mut cross_axis_max: f32 = 0.0;
|
||||||
for child in &mut self.children {
|
for child in &mut self.children {
|
||||||
if let Some(flex) = Self::child_flex(&child) {
|
if let Some(metadata) = child.metadata::<FlexParentData>() {
|
||||||
total_flex += flex;
|
total_flex += metadata.flex;
|
||||||
} else {
|
} else {
|
||||||
let child_constraint = match self.axis {
|
let child_constraint = match self.axis {
|
||||||
Axis::Horizontal => SizeConstraint::new(
|
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_space = constraint.max_along(self.axis) - fixed_space;
|
||||||
let mut remaining_flex = total_flex;
|
let mut remaining_flex = total_flex;
|
||||||
for child in &mut self.children {
|
self.layout_flex_children(
|
||||||
if let Some(flex) = Self::child_flex(&child) {
|
false,
|
||||||
let child_max = if remaining_flex == 0.0 {
|
constraint,
|
||||||
remaining_space
|
&mut remaining_space,
|
||||||
} else {
|
&mut remaining_flex,
|
||||||
let space_per_flex = remaining_space / remaining_flex;
|
&mut cross_axis_max,
|
||||||
space_per_flex * flex
|
cx,
|
||||||
};
|
);
|
||||||
let child_constraint = match self.axis {
|
self.layout_flex_children(
|
||||||
Axis::Horizontal => SizeConstraint::new(
|
true,
|
||||||
vec2f(0.0, constraint.min.y()),
|
constraint,
|
||||||
vec2f(child_max, constraint.max.y()),
|
&mut remaining_space,
|
||||||
),
|
&mut remaining_flex,
|
||||||
Axis::Vertical => SizeConstraint::new(
|
&mut cross_axis_max,
|
||||||
vec2f(constraint.min.x(), 0.0),
|
cx,
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.axis {
|
match self.axis {
|
||||||
Axis::Horizontal => vec2f(constraint.max.x() - remaining_space, cross_axis_max),
|
Axis::Horizontal => vec2f(constraint.max.x() - remaining_space, cross_axis_max),
|
||||||
|
@ -181,6 +211,7 @@ impl Element for Flex {
|
||||||
|
|
||||||
struct FlexParentData {
|
struct FlexParentData {
|
||||||
flex: f32,
|
flex: f32,
|
||||||
|
expanded: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Expanded {
|
pub struct Expanded {
|
||||||
|
@ -191,7 +222,10 @@ pub struct Expanded {
|
||||||
impl Expanded {
|
impl Expanded {
|
||||||
pub fn new(flex: f32, child: ElementBox) -> Self {
|
pub fn new(flex: f32, child: ElementBox) -> Self {
|
||||||
Expanded {
|
Expanded {
|
||||||
metadata: FlexParentData { flex },
|
metadata: FlexParentData {
|
||||||
|
flex,
|
||||||
|
expanded: true,
|
||||||
|
},
|
||||||
child,
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -964,13 +964,13 @@ impl View for Workspace {
|
||||||
if let Some(element) =
|
if let Some(element) =
|
||||||
self.left_sidebar.render_active_item(&settings, cx)
|
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());
|
content.add_child(Expanded::new(1.0, self.center.render()).boxed());
|
||||||
if let Some(element) =
|
if let Some(element) =
|
||||||
self.right_sidebar.render_active_item(&settings, cx)
|
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.add_child(self.right_sidebar.render(&settings, cx));
|
||||||
content.boxed()
|
content.boxed()
|
||||||
|
|
|
@ -191,7 +191,7 @@ impl Pane {
|
||||||
let border = &theme.workspace.tab.container.border;
|
let border = &theme.workspace.tab.container.border;
|
||||||
|
|
||||||
row.add_child(
|
row.add_child(
|
||||||
Expanded::new(
|
Flexible::new(
|
||||||
1.0,
|
1.0,
|
||||||
MouseEventHandler::new::<Tab, _, _, _>(item.id(), cx, |mouse_state, cx| {
|
MouseEventHandler::new::<Tab, _, _, _>(item.id(), cx, |mouse_state, cx| {
|
||||||
let title = item.title(cx);
|
let title = item.title(cx);
|
||||||
|
|
|
@ -113,8 +113,12 @@ impl Sidebar {
|
||||||
container.add_child(self.render_resize_handle(settings, cx));
|
container.add_child(self.render_resize_handle(settings, cx));
|
||||||
}
|
}
|
||||||
container.add_child(
|
container.add_child(
|
||||||
|
Flexible::new(
|
||||||
|
1.,
|
||||||
ConstrainedBox::new(ChildView::new(active_item.id()).boxed())
|
ConstrainedBox::new(ChildView::new(active_item.id()).boxed())
|
||||||
.with_width(*self.width.borrow())
|
.with_max_width(*self.width.borrow())
|
||||||
|
.boxed(),
|
||||||
|
)
|
||||||
.boxed(),
|
.boxed(),
|
||||||
);
|
);
|
||||||
if matches!(self.side, Side::Left) {
|
if matches!(self.side, Side::Left) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue