//! 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( render: impl 'static + FnOnce(Size, &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, &mut Window, &mut App) -> AnyElement>>, style: StyleRefinement, } impl Element for ContainerQuery { type RequestLayoutState = (); type PrepaintState = Option; fn id(&self) -> Option { 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, _request_layout: &mut Self::RequestLayoutState, window: &mut Window, cx: &mut App, ) -> Option { 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, _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 } }