diff --git a/crates/chat_panel/src/chat_panel.rs b/crates/chat_panel/src/chat_panel.rs index 452f041c7b..a7c9123894 100644 --- a/crates/chat_panel/src/chat_panel.rs +++ b/crates/chat_panel/src/chat_panel.rs @@ -219,7 +219,7 @@ impl ChatPanel { Empty::new().boxed() }; - Flexible::new(1., true, messages).boxed() + FlexItem::new(messages).flex(1., true).boxed() } fn render_message(&self, message: &ChannelMessage, cx: &AppContext) -> ElementBox { diff --git a/crates/contacts_panel/src/contacts_panel.rs b/crates/contacts_panel/src/contacts_panel.rs index b8b5b3a361..06c6b8f1bb 100644 --- a/crates/contacts_panel/src/contacts_panel.rs +++ b/crates/contacts_panel/src/contacts_panel.rs @@ -212,7 +212,7 @@ impl ContactsPanel { })); } }) - .flexible(1., true) + .flex(1., true) .boxed() }) .constrained() diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index 9f0137ef62..56fd255d82 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -78,7 +78,11 @@ impl View for FileFinder { .with_style(settings.theme.selector.input_editor.container) .boxed(), ) - .with_child(Flexible::new(1.0, false, self.render_matches(cx)).boxed()) + .with_child( + FlexItem::new(self.render_matches(cx)) + .flex(1., false) + .boxed(), + ) .boxed(), ) .with_style(settings.theme.selector.container) @@ -166,23 +170,19 @@ impl FileFinder { // .boxed(), // ) .with_child( - Flexible::new( - 1.0, - false, - Flex::column() - .with_child( - Label::new(file_name.to_string(), style.label.clone()) - .with_highlights(file_name_positions) - .boxed(), - ) - .with_child( - Label::new(full_path, style.label.clone()) - .with_highlights(full_path_positions) - .boxed(), - ) - .boxed(), - ) - .boxed(), + Flex::column() + .with_child( + Label::new(file_name.to_string(), style.label.clone()) + .with_highlights(file_name_positions) + .boxed(), + ) + .with_child( + Label::new(full_path, style.label.clone()) + .with_highlights(full_path_positions) + .boxed(), + ) + .flex(1., false) + .boxed(), ) .boxed(), ) diff --git a/crates/gpui/src/elements.rs b/crates/gpui/src/elements.rs index 6830923953..79112863b5 100644 --- a/crates/gpui/src/elements.rs +++ b/crates/gpui/src/elements.rs @@ -139,11 +139,18 @@ pub trait Element { Expanded::new(self.boxed()) } - fn flexible(self, flex: f32, expanded: bool) -> Flexible + fn flex(self, flex: f32, expanded: bool) -> FlexItem where Self: 'static + Sized, { - Flexible::new(flex, expanded, self.boxed()) + FlexItem::new(self.boxed()).flex(flex, expanded) + } + + fn flex_float(self) -> FlexItem + where + Self: 'static + Sized, + { + FlexItem::new(self.boxed()).float() } } diff --git a/crates/gpui/src/elements/flex.rs b/crates/gpui/src/elements/flex.rs index 6b884289a2..2ec307bbc3 100644 --- a/crates/gpui/src/elements/flex.rs +++ b/crates/gpui/src/elements/flex.rs @@ -34,7 +34,7 @@ impl Flex { fn layout_flex_children( &mut self, - expanded: bool, + layout_expanded: bool, constraint: SizeConstraint, remaining_space: &mut f32, remaining_flex: &mut f32, @@ -44,32 +44,33 @@ impl Flex { let cross_axis = self.axis.invert(); for child in &mut self.children { if let Some(metadata) = child.metadata::() { - if metadata.expanded != expanded { - continue; - } + if let Some((flex, expanded)) = metadata.flex { + if expanded != layout_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)); + 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)); + } } } } @@ -82,7 +83,7 @@ impl Extend for Flex { } impl Element for Flex { - type LayoutState = bool; + type LayoutState = f32; type PaintState = (); fn layout( @@ -96,8 +97,11 @@ 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(metadata) = child.metadata::() { - *total_flex.get_or_insert(0.) += metadata.flex; + if let Some(flex) = child + .metadata::() + .and_then(|metadata| metadata.flex.map(|(flex, _)| flex)) + { + *total_flex.get_or_insert(0.) += flex; } else { let child_constraint = match self.axis { Axis::Horizontal => SizeConstraint::new( @@ -115,12 +119,12 @@ impl Element for Flex { } } + let mut remaining_space = constraint.max_along(self.axis) - fixed_space; let mut size = if let Some(mut remaining_flex) = total_flex { - if constraint.max_along(self.axis).is_infinite() { + if remaining_space.is_infinite() { panic!("flex contains flexible children but has an infinite constraint along the flex axis"); } - let mut remaining_space = constraint.max_along(self.axis) - fixed_space; self.layout_flex_children( false, constraint, @@ -156,38 +160,47 @@ impl Element for Flex { size.set_y(size.y().max(constraint.min.y())); } - let mut overflowing = false; if size.x() > constraint.max.x() { size.set_x(constraint.max.x()); - overflowing = true; } if size.y() > constraint.max.y() { size.set_y(constraint.max.y()); - overflowing = true; } - (size, overflowing) + (size, remaining_space) } fn paint( &mut self, bounds: RectF, visible_bounds: RectF, - overflowing: &mut Self::LayoutState, + remaining_space: &mut Self::LayoutState, cx: &mut PaintContext, ) -> Self::PaintState { - if *overflowing { + let overflowing = *remaining_space < 0.; + if overflowing { cx.scene.push_layer(Some(bounds)); } let mut child_origin = bounds.origin(); for child in &mut self.children { + if *remaining_space > 0. { + if let Some(metadata) = child.metadata::() { + if metadata.float { + match self.axis { + Axis::Horizontal => child_origin += vec2f(*remaining_space, 0.0), + Axis::Vertical => child_origin += vec2f(0.0, *remaining_space), + } + *remaining_space = 0.; + } + } + } child.paint(child_origin, visible_bounds, cx); match self.axis { Axis::Horizontal => child_origin += vec2f(child.size().x(), 0.0), Axis::Vertical => child_origin += vec2f(0.0, child.size().y()), } } - if *overflowing { + if overflowing { cx.scene.pop_layer(); } } @@ -224,25 +237,38 @@ impl Element for Flex { } struct FlexParentData { - flex: f32, - expanded: bool, + flex: Option<(f32, bool)>, + float: bool, } -pub struct Flexible { +pub struct FlexItem { metadata: FlexParentData, child: ElementBox, } -impl Flexible { - pub fn new(flex: f32, expanded: bool, child: ElementBox) -> Self { - Flexible { - metadata: FlexParentData { flex, expanded }, +impl FlexItem { + pub fn new(child: ElementBox) -> Self { + FlexItem { + metadata: FlexParentData { + flex: None, + float: false, + }, child, } } + + pub fn flex(mut self, flex: f32, expanded: bool) -> Self { + self.metadata.flex = Some((flex, expanded)); + self + } + + pub fn float(mut self) -> Self { + self.metadata.float = true; + self + } } -impl Element for Flexible { +impl Element for FlexItem { type LayoutState = (); type PaintState = (); diff --git a/crates/outline/src/outline.rs b/crates/outline/src/outline.rs index a626ff89c8..c33cb60b3e 100644 --- a/crates/outline/src/outline.rs +++ b/crates/outline/src/outline.rs @@ -77,7 +77,11 @@ impl View for OutlineView { .with_style(settings.theme.selector.input_editor.container) .boxed(), ) - .with_child(Flexible::new(1.0, false, self.render_matches(cx)).boxed()) + .with_child( + FlexItem::new(self.render_matches(cx)) + .flex(1.0, false) + .boxed(), + ) .contained() .with_style(settings.theme.selector.container) .constrained() diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index 34d5306d99..74e7d90d68 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -76,7 +76,11 @@ impl View for ProjectSymbolsView { .with_style(settings.theme.selector.input_editor.container) .boxed(), ) - .with_child(Flexible::new(1.0, false, self.render_matches(cx)).boxed()) + .with_child( + FlexItem::new(self.render_matches(cx)) + .flex(1., false) + .boxed(), + ) .contained() .with_style(settings.theme.selector.container) .constrained() diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index caf25cbec1..90d2876092 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -82,11 +82,7 @@ impl View for BufferSearchBar { Flex::row() .with_child( Flex::row() - .with_child( - ChildView::new(&self.query_editor) - .flexible(1., true) - .boxed(), - ) + .with_child(ChildView::new(&self.query_editor).flex(1., true).boxed()) .with_children(self.active_editor.as_ref().and_then(|editor| { let matches = self.editors_with_matches.get(&editor.downgrade())?; let message = if let Some(match_ix) = self.active_match_index { diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 293c33ae6a..009c67ba93 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -164,12 +164,10 @@ impl View for ProjectSearchView { .aligned() .contained() .with_background_color(theme.editor.background) - .flexible(1., true) + .flex(1., true) .boxed() } else { - ChildView::new(&self.results_editor) - .flexible(1., true) - .boxed() + ChildView::new(&self.results_editor).flex(1., true).boxed() } } @@ -691,11 +689,7 @@ impl View for ProjectSearchBar { Flex::row() .with_child( Flex::row() - .with_child( - ChildView::new(&search.query_editor) - .flexible(1., true) - .boxed(), - ) + .with_child(ChildView::new(&search.query_editor).flex(1., true).boxed()) .with_children(search.active_match_index.map(|match_ix| { Label::new( format!( diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index 725319be41..d61cac1c44 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -310,7 +310,11 @@ impl View for ThemeSelector { .with_style(theme.selector.input_editor.container) .boxed(), ) - .with_child(Flexible::new(1.0, false, self.render_matches(cx)).boxed()) + .with_child( + FlexItem::new(self.render_matches(cx)) + .flex(1., false) + .boxed(), + ) .boxed(), ) .with_style(theme.selector.container) diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index ec9319a396..d48a5711a3 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -612,7 +612,7 @@ impl Pane { Empty::new() .contained() .with_border(theme.workspace.tab.container.border) - .flexible(0., true) + .flex(0., true) .named("filler"), ); @@ -641,7 +641,7 @@ impl View for Pane { Flex::column() .with_child(self.render_tabs(cx)) .with_child(ChildView::new(&self.toolbar).boxed()) - .with_child(ChildView::new(active_item).flexible(1., true).boxed()) + .with_child(ChildView::new(active_item).flex(1., true).boxed()) .boxed() } else { Empty::new().boxed() diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index afffec5074..258d644148 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -248,7 +248,7 @@ impl PaneAxis { member = Container::new(member).with_border(border).boxed(); } - Flexible::new(1.0, true, member).boxed() + FlexItem::new(member).flex(1.0, true).boxed() })) .boxed() } diff --git a/crates/workspace/src/sidebar.rs b/crates/workspace/src/sidebar.rs index 46713424e6..7a7ad4e272 100644 --- a/crates/workspace/src/sidebar.rs +++ b/crates/workspace/src/sidebar.rs @@ -138,7 +138,7 @@ impl Sidebar { let width = self.width.clone(); move |size, _| *width.borrow_mut() = size.x() }) - .flexible(1., false) + .flex(1., false) .boxed(), ); if matches!(self.side, Side::Left) { diff --git a/crates/workspace/src/status_bar.rs b/crates/workspace/src/status_bar.rs index 4d00591787..a91dd645a0 100644 --- a/crates/workspace/src/status_bar.rs +++ b/crates/workspace/src/status_bar.rs @@ -47,12 +47,12 @@ impl View for StatusBar { .with_margin_right(theme.item_spacing) .boxed() })) - .with_child(Empty::new().flexible(1., true).boxed()) .with_children(self.right_items.iter().map(|i| { ChildView::new(i.as_ref()) .aligned() .contained() .with_margin_left(theme.item_spacing) + .flex_float() .boxed() })) .contained() diff --git a/crates/workspace/src/toolbar.rs b/crates/workspace/src/toolbar.rs index a709f4bd05..ed3370c315 100644 --- a/crates/workspace/src/toolbar.rs +++ b/crates/workspace/src/toolbar.rs @@ -46,12 +46,12 @@ impl View for Toolbar { .with_margin_right(theme.item_spacing) .boxed() })) - .with_child(Empty::new().flexible(1., true).boxed()) .with_children(self.right_items.iter().map(|i| { ChildView::new(i.as_ref()) .aligned() .contained() .with_margin_left(theme.item_spacing) + .flex_float() .boxed() })) .contained() diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index d3a644d852..2a6a3c6355 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1946,36 +1946,35 @@ impl View for Workspace { if let Some(element) = self.left_sidebar.render_active_item(&theme, cx) { - content.add_child(Flexible::new(0.8, false, element).boxed()); + content + .add_child(FlexItem::new(element).flex(0.8, false).boxed()); } content.add_child( Flex::column() .with_child( - Flexible::new( - 1., - true, - self.center.render( - &theme, - &self.follower_states_by_leader, - self.project.read(cx).collaborators(), - ), - ) + FlexItem::new(self.center.render( + &theme, + &self.follower_states_by_leader, + self.project.read(cx).collaborators(), + )) + .flex(1., true) .boxed(), ) .with_child(ChildView::new(&self.status_bar).boxed()) - .flexible(1., true) + .flex(1., true) .boxed(), ); if let Some(element) = self.right_sidebar.render_active_item(&theme, cx) { - content.add_child(Flexible::new(0.8, false, element).boxed()); + content + .add_child(FlexItem::new(element).flex(0.8, false).boxed()); } content.add_child(self.right_sidebar.render(&theme, cx)); content.boxed() }) .with_children(self.modal.as_ref().map(|m| ChildView::new(m).boxed())) - .flexible(1.0, true) + .flex(1.0, true) .boxed(), ) .contained()