Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions crates/gpui/src/taffy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,36 @@ impl TaffyLayoutEngine {
.into()
}

/// Treats any `auto` dimension of the given node's style as filling `size`.
///
/// This is applied to window roots before layout so they behave like the
/// root element on the web, which stretches to fill the initial containing
/// block (the viewport) unless given an explicit size. Explicitly styled
/// dimensions are preserved.
pub fn stretch_auto_size_to_fill(
&mut self,
id: LayoutId,
size: Size<Pixels>,
scale_factor: f32,
) {
let style = self.taffy.style(id.0).expect(EXPECT_MESSAGE);
let stretch_width = style.size.width.is_auto();
let stretch_height = style.size.height.is_auto();
if !stretch_width && !stretch_height {
return;
}
let mut style = style.clone();
if stretch_width {
style.size.width =
taffy::style::Dimension::length(round_to_device_pixel(size.width.0, scale_factor));
}
if stretch_height {
style.size.height =
taffy::style::Dimension::length(round_to_device_pixel(size.height.0, scale_factor));
}
self.taffy.set_style(id.0, style).expect(EXPECT_MESSAGE);
}

// Used to understand performance
#[allow(dead_code)]
fn count_all_children(&self, parent: LayoutId) -> anyhow::Result<u32> {
Expand Down
89 changes: 88 additions & 1 deletion crates/gpui/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2823,8 +2823,16 @@ impl Window {
}
};

// Layout all root elements.
// Layout all root elements. Like the root element on the web, which
// stretches to fill the viewport unless explicitly sized, window roots
// fill the window when their size is `auto`.
let scale_factor = self.scale_factor();
let mut root_element = self.root.as_ref().unwrap().clone().into_any_element();
let root_layout_id = root_element.request_layout(self, cx);
self.layout_engine
.as_mut()
.unwrap()
.stretch_auto_size_to_fill(root_layout_id, root_size, scale_factor);
root_element.prepaint_as_root(Point::default(), root_size.into(), self, cx);

#[cfg(any(feature = "inspector", debug_assertions))]
Expand All @@ -2837,6 +2845,11 @@ impl Window {
let mut tooltip_element = None;
if let Some(prompt) = self.prompt.take() {
let mut element = prompt.view.any_view().into_any_element();
let prompt_layout_id = element.request_layout(self, cx);
self.layout_engine
.as_mut()
.unwrap()
.stretch_auto_size_to_fill(prompt_layout_id, root_size, scale_factor);
element.prepaint_as_root(Point::default(), root_size.into(), self, cx);
prompt_element = Some(element);
self.prompt = Some(prompt);
Expand Down Expand Up @@ -6312,3 +6325,77 @@ pub fn outline(
border_style,
}
}

#[cfg(test)]
mod tests {
use crate::{
AppContext as _, Bounds, Context, IntoElement, ParentElement as _, Pixels, Render,
Styled as _, TestAppContext, Window, canvas, div, px, size,
};
use std::{cell::Cell, rc::Rc};

struct RootView {
explicit_size: bool,
child_bounds: Rc<Cell<Bounds<Pixels>>>,
}

impl Render for RootView {
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
let child_bounds = self.child_bounds.clone();
let root = div().flex().flex_col().child(
canvas(
move |bounds, _, _| child_bounds.set(bounds),
|_, _, _, _| {},
)
.size_full(),
);
if self.explicit_size {
root.w(px(300.)).h(px(200.))
} else {
root
}
}
}

#[test]
fn auto_sized_window_root_fills_the_window() {
let mut cx = TestAppContext::single();
let child_bounds = Rc::new(Cell::new(Bounds::default()));
let window = cx.add_window({
let child_bounds = child_bounds.clone();
move |_, _| RootView {
explicit_size: false,
child_bounds,
}
});

let viewport_size = cx
.update_window(window.into(), |_, window, cx| {
window.draw(cx).clear();
window.viewport_size()
})
.unwrap();

assert_eq!(child_bounds.get().size, viewport_size);
}

#[test]
fn explicitly_sized_window_root_keeps_its_size() {
let mut cx = TestAppContext::single();
let child_bounds = Rc::new(Cell::new(Bounds::default()));
let window = cx.add_window({
let child_bounds = child_bounds.clone();
move |_, _| RootView {
explicit_size: true,
child_bounds,
}
});

cx.update_window(window.into(), |_, window, cx| {
window.draw(cx).clear();
})
.unwrap();

assert_eq!(child_bounds.get().size, size(px(300.), px(200.)));
}
}
Loading