Allow flex items to float to the end of the flex axis

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra 2022-03-30 16:38:00 +02:00
parent 621e67bca7
commit 0453dd1101
16 changed files with 138 additions and 104 deletions

View file

@ -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 {

View file

@ -212,7 +212,7 @@ impl ContactsPanel {
}));
}
})
.flexible(1., true)
.flex(1., true)
.boxed()
})
.constrained()

View file

@ -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(),
)

View file

@ -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()
}
}

View file

@ -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::<FlexParentData>() {
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<ElementBox> 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::<FlexParentData>() {
*total_flex.get_or_insert(0.) += metadata.flex;
if let Some(flex) = child
.metadata::<FlexParentData>()
.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::<FlexParentData>() {
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 = ();

View file

@ -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()

View file

@ -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()

View file

@ -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 {

View file

@ -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!(

View file

@ -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)

View file

@ -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()

View file

@ -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()
}

View file

@ -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) {

View file

@ -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()

View file

@ -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()

View file

@ -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()