From a0dd41cdf65859bc61b4b5ead1b1546de50cc414 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 3 Sep 2021 11:40:18 +0200 Subject: [PATCH] Add a `Flexible` element that works like in Flutter --- gpui/src/elements/flex.rs | 162 ++++++++++++++++++++++++++++------- zed/src/workspace.rs | 4 +- zed/src/workspace/pane.rs | 2 +- zed/src/workspace/sidebar.rs | 10 ++- 4 files changed, 143 insertions(+), 35 deletions(-) diff --git a/gpui/src/elements/flex.rs b/gpui/src/elements/flex.rs index 58e603fc6f..a71407cc25 100644 --- a/gpui/src/elements/flex.rs +++ b/gpui/src/elements/flex.rs @@ -32,8 +32,46 @@ impl Flex { Self::new(Axis::Vertical) } - fn child_flex<'b>(child: &ElementBox) -> Option { - child.metadata::().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::() { + 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::() { + 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) + }) + } +} diff --git a/zed/src/workspace.rs b/zed/src/workspace.rs index e45c5d7d1b..f5c9e0e58e 100644 --- a/zed/src/workspace.rs +++ b/zed/src/workspace.rs @@ -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() diff --git a/zed/src/workspace/pane.rs b/zed/src/workspace/pane.rs index 25afac91b0..3894ad4cbd 100644 --- a/zed/src/workspace/pane.rs +++ b/zed/src/workspace/pane.rs @@ -191,7 +191,7 @@ impl Pane { let border = &theme.workspace.tab.container.border; row.add_child( - Expanded::new( + Flexible::new( 1.0, MouseEventHandler::new::(item.id(), cx, |mouse_state, cx| { let title = item.title(cx); diff --git a/zed/src/workspace/sidebar.rs b/zed/src/workspace/sidebar.rs index e415551a8f..7dbc30dfbf 100644 --- a/zed/src/workspace/sidebar.rs +++ b/zed/src/workspace/sidebar.rs @@ -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));