Files
zed/crates/gpui/src/elements/container_query.rs
T
Mikayla Makiandzed-zippy[bot] 49ad06c1b4 gpui: Add container_query element (#60774)
Adds a `container_query` element to GPUI, in the spirit of CSS container
queries: the element's own size is determined solely by its style and
the space offered by its parent, and once that size is known the
provided closure is called with the measured size to build the contents.

```rust
container_query(|size, _window, _cx| {
    if size.width < px(240.) {
        div().child("Narrow layout")
    } else {
        div().child("Wide layout")
    }
});
```

Implementation notes:

- Defaults to filling its parent (`size_full()` semantics), overridable
via `Styled` since contents can't influence the container's size (the
same constraint CSS container queries impose).
- Reworks the `grid_layout` example (the Holy Grail layout) to
demonstrate it: the three-column grid collapses to a stacked column when
the window is narrower than 400px, and the header shows the live
measured width.

Release Notes:

- N/A

---------

Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
2026-07-12 03:38:24 +00:00

127 lines
3.7 KiB
Rust

//! A container query element, in the spirit of CSS container queries.
//! The element's own size is determined solely by its style and the space
//! offered by its parent.
use refineable::Refineable as _;
use crate::{
AnyElement, App, AvailableSpace, Bounds, Element, ElementId, GlobalElementId,
InspectorElementId, IntoElement, LayoutId, Pixels, Size, Style, StyleRefinement, Styled,
Window, relative,
};
/// Construct a container query element with the given render callback.
/// The callback receives the size the element was assigned during layout and
/// returns the contents to display within it.
///
/// By default the element fills its parent (equivalent to `.size_full()`);
/// use the [`Styled`] methods to size it differently. Because the contents
/// don't exist until after layout, they cannot influence the element's size.
///
/// # Example
///
/// ```
/// # use gpui::{container_query, div, px, IntoElement, ParentElement};
/// container_query(|size, _window, _cx| {
/// if size.width < px(240.) {
/// div().child("Narrow layout")
/// } else {
/// div().child("Wide layout")
/// }
/// });
/// ```
pub fn container_query<E>(
render: impl 'static + FnOnce(Size<Pixels>, &mut Window, &mut App) -> E,
) -> ContainerQuery
where
E: IntoElement,
{
let mut base_style = StyleRefinement::default();
base_style.size.width = Some(relative(1.).into());
base_style.size.height = Some(relative(1.).into());
ContainerQuery {
render: Some(Box::new(|size, window, cx| {
render(size, window, cx).into_any_element()
})),
style: base_style,
}
}
/// A container query element, created with [`container_query`].
pub struct ContainerQuery {
render: Option<Box<dyn FnOnce(Size<Pixels>, &mut Window, &mut App) -> AnyElement>>,
style: StyleRefinement,
}
impl Element for ContainerQuery {
type RequestLayoutState = ();
type PrepaintState = Option<AnyElement>;
fn id(&self) -> Option<ElementId> {
None
}
fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
None
}
fn request_layout(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
window: &mut Window,
cx: &mut App,
) -> (LayoutId, Self::RequestLayoutState) {
let mut style = Style::default();
style.refine(&self.style);
let layout_id = window.request_layout(style, [], cx);
(layout_id, ())
}
fn prepaint(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
window: &mut Window,
cx: &mut App,
) -> Option<AnyElement> {
let render = self.render.take()?;
let mut child = render(bounds.size, window, cx);
child.layout_as_root(bounds.size.map(AvailableSpace::Definite), window, cx);
child.prepaint_at(bounds.origin, window, cx);
Some(child)
}
fn paint(
&mut self,
_id: Option<&GlobalElementId>,
_inspector_id: Option<&InspectorElementId>,
_bounds: Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
prepaint: &mut Self::PrepaintState,
window: &mut Window,
cx: &mut App,
) {
if let Some(child) = prepaint {
child.paint(window, cx);
}
}
}
impl IntoElement for ContainerQuery {
type Element = Self;
fn into_element(self) -> Self::Element {
self
}
}
impl Styled for ContainerQuery {
fn style(&mut self) -> &mut StyleRefinement {
&mut self.style
}
}