mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-11 13:10:54 +00:00
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:
parent
621e67bca7
commit
0453dd1101
16 changed files with 138 additions and 104 deletions
|
@ -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 {
|
||||
|
|
|
@ -212,7 +212,7 @@ impl ContactsPanel {
|
|||
}));
|
||||
}
|
||||
})
|
||||
.flexible(1., true)
|
||||
.flex(1., true)
|
||||
.boxed()
|
||||
})
|
||||
.constrained()
|
||||
|
|
|
@ -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(),
|
||||
)
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 = ();
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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!(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue