GPUI Box GitHub

Components / layout

Responsive builder

A container that builds its content from its own measured width.

A builder is RenderOnce. Construct and mount it in one expression.

use gpui_kit::layout::Responsive;

Reviewed in responsive.

Also drawn by adaptive-navigation, which arranges components rather than reviewing them.

Construct
new(ident: impl Into<Ident>, build: impl FnOnce(ContainerSize, &mut Window, &mut App) -> AnyElement + 'static) -> Self
Options

Chain onto the value.

fill() -> Self
Example · responsive
fn responsive(_window: &mut Window, cx: &mut App) -> AnyElement {
    let theme = cx.theme().clone();
    let arrangement = move |id: &'static str| {
        let theme = theme.clone();
        move |size: ContainerSize, _: &mut Window, cx: &mut App| {
            let wide = size.width().is_some_and(|measured| measured >= 480.0);
            // The unmeasured frame is a real state and it is named rather than
            // guessed, which is the whole argument the component makes.
            let heading = match size.width() {
                None => "Not laid out yet, so neither arrangement is chosen".to_string(),
                Some(measured) if wide => format!("{measured:.0}px wide, so two columns"),
                Some(measured) => format!("{measured:.0}px wide, so one column"),
            };
            // A card rather than a tinted rectangle, and with enough in it to
            // be a column: two panels holding one word each say nothing about
            // an arrangement, which is what the component is for.
            let block = |label: &'static str, lines: &'static [&'static str], cx: &mut App| {
                div()
                    .flex_1()
                    .min_w_0()
                    .column()
                    .gap_token(&theme, Space::Xs)
                    .p_token(&theme, Space::Md)
                    .card_surface(&theme, CardVariant::Elevated)
                    .child(crate::foundation::text(&theme, TypeScale::Label, label))
                    .children(lines.iter().map(|line| {
                        crate::foundation::text(&theme, TypeScale::Caption, *line)
                            .text_tone(&theme, TextTone::Muted)
                    }))
                    .semantic_in(
                        cx,
                        NodeSpec::new(format!("{id}.{}", label.to_lowercase()), Role::Group)
                            .text(label),
                    )
            };
            let panes = div()
                .gap_token(&theme, Space::Sm)
                .child(block(
                    "Settings",
                    &["Theme", "Density", "Reading direction"],
                    cx,
                ))
                .child(block(
                    "Detail",
                    &["Studio Dark", "Comfortable", "Left to right"],
                    cx,
                ));
            div()
                .column()
                .gap_token(&theme, Space::Sm)
                .child(
                    crate::foundation::text(&theme, TypeScale::Caption, heading.clone())
                        .text_tone(&theme, TextTone::Faint),
                )
                .child(if wide { panes.row() } else { panes.column() })
                .semantic_in(cx, NodeSpec::new(id, Role::Group).value(heading))
                .into_any_element()
        }
    };

    let theme = cx.theme().clone();
    stack(&theme)
        .child(caption(&theme, "Wide enough for two columns"))
        .child(div().w(px(620.0)).child(Responsive::new(
            "scene.responsive.wide",
            arrangement("scene.responsive.wide.body"),
        )))
        .child(caption(&theme, "The same content, narrow"))
        .child(div().w(px(320.0)).child(Responsive::new(
            "scene.responsive.narrow",
            arrangement("scene.responsive.narrow.body"),
        )))
        .into_any_element()
}

Source: crates/gpui-kit/src/layout/responsive.rs