Collapsible

An interactive element which expands/collapses.

Install

#Import

use gpui_kit::component::collapsible::Collapsible;
// Installed in ui/components. That directory is on the Slint include path, so import by file name:
import { Collapsible } from "collapsible.slint";
use iced::widget::{
use iced::{Alignment, Background, Border, Color, Element, Length, Padding, Task, Theme};

#Usage

#Details

Collapsible::new()
    .max_w_128()
    .gap_1()
    .open(self.open)
    .child(
        "This is a collapsible component. \
        Click the header to expand or collapse the content.",
    )
    .content(
        "This is the full content of the Collapsible component. \
        It is only visible when the component is expanded. \n\
        You can put any content you like here, including text, images, \
        or other UI elements.",
    )
    .child(
        h_flex().justify_center().child(
            Button::new("toggle1")
                .icon(IconName::ChevronDown)
                .label("Show more")
                .when(open, |this| {
                    this.icon(IconName::ChevronUp).label("Show less")
                })
                .xsmall()
                .link()
                .on_click({
                    cx.listener(move |this, _, _, cx| {
                        this.open = !this.open;
                        cx.notify();
                    })
                }),
        ),
    )
Collapsible {
    title: "Advanced";
    open: true;
    Text {
        text: "These options are for power users.";
        color: Theme.foreground;
        wrap: word-wrap;
    }
}
// The titled trigger form, open on boot: the title and the chevron are the
// component's own, and the caller still owns the state and the message.
let details = Collapsible::new()
    .trigger(collapsible::trigger(
        "Advanced",
        app.advanced_open,
        Message::ToggleAdvanced,
    ))
    .open(app.advanced_open)
    .content(line(
        "These options are for power users.",
        theme::FONT_SIZE_SM,
        |theme| Tokens::of(theme).foreground,
    ));

We can use open method to control the collapsed state. If false, the content method added child elements will be hidden.

#Animated reveal

GPUINo GPUI version of this example yet.
SlintNo Slint version of this example yet.
// `animated(true)` measures the panel and moves it from nothing to its full
// height over 150ms — the duration the Slint component gives `animate
// height`. The content stays mounted while closed, so pressing the trigger
// again mid-flight reverses the reveal from the height it has already
// reached instead of restarting it.
let animated = Collapsible::new()
    .trigger(collapsible::trigger(
        "Advanced options",
        app.animated_open,
        Message::ToggleAnimated,
    ))
    .open(app.animated_open)
    .animated(true)
    .content(
        column![
            line(
                "The panel is measured from its content, then clipped to \
                 however far the reveal has come.",
                theme::FONT_SIZE_SM,
                muted,
            ),
            line(
                "Toggle it again while it moves and it turns around from \
                 where it is.",
                theme::FONT_SIZE_SM,
                muted,
            ),
        ]
        .spacing(8.0),
    );

Opt into a reversible, measured height reveal with a stable motion ID:

Collapsible::new()
    .motion_id("advanced-options")
    .open(self.open)
    .content(options)
SlintNo Slint version of this example yet.

The content remains mounted while closed so it can be measured and immediately reverse if toggled mid-animation. Without motion_id, the component keeps the immediate mount/unmount behavior. See the GPUI Base Motion guide for timing, reduced-motion, and performance details.

#Basic

GPUINo GPUI version of this example yet.
SlintNo Slint version of this example yet.
// The trigger sits beside the title and the summary stays visible: the ghost
// chevron button is the panel's trigger, and the status row never moves.
let basic_header = row![
    text("Order #4189")
        .size(theme::FONT_SIZE_SM)
        .font(iced::Font {
            weight: iced::font::Weight::Semibold,
            ..iced::Font::DEFAULT
        })
        .width(Length::Fill)
        .style(|theme: &Theme| text::Style {
            color: Some(Tokens::of(theme).foreground),
        }),
    icon_button(CHEVRONS_UP_DOWN, Message::ToggleBasic),
]
.spacing(8.0)
.align_y(Alignment::Center);

let basic = column![
    basic_header,
    soft_card(
        row![
            line("Status", theme::FONT_SIZE_SM, muted).width(Length::Fill),
            Tag::new("Shipped")
                .variant(TagVariant::Status)
                .status(Status::Success)
                .size(ControlSize::Small),
        ]
        .spacing(8.0)
        .align_y(Alignment::Center),
    ),
    Collapsible::new().open(app.basic_open).content(
        column![
            card(
                column![
                    line("Shipping address", theme::FONT_SIZE_SM, |theme| Tokens::of(
                        theme
                    )
                    .foreground),
                    line("100 Market St, San Francisco", theme::FONT_SIZE_SM, muted),
                ]
                .spacing(4.0)
            ),
            card(
                column![
                    line("Items", theme::FONT_SIZE_SM, |theme| Tokens::of(theme)
                        .foreground),
                    line("2x Studio Headphones", theme::FONT_SIZE_SM, muted),
                ]
                .spacing(4.0)
            ),
        ]
        .spacing(8.0),
    ),
]
.spacing(8.0);

A trigger beside the title, with a summary that stays visible.

Collapsible::new()
    .open(self.is_open("order"))
    .child(
        h_flex()
            .justify_between()
            .child("Order #4189")
            .child(
                Button::new("order")
                    .ghost()
                    .xsmall()
                    .icon(IconName::ChevronsUpDown)
                    .on_click(cx.listener(|this, _, _, cx| this.toggle("order", cx))),
            ),
    )
    .child(status_row)
    .content(order_details)
VerticalLayout {
    spacing: 8px;
    HorizontalLayout {
        spacing: 8px;
        alignment: center;
        Text {
            horizontal-stretch: 1;
            text: "Order #4189";
            color: Theme.foreground;
            font-size: Theme.font-size-sm;
            font-weight: 600;
            vertical-alignment: center;
        }
        Button {
            icon: "chevrons-up-down";
            variant: ButtonVariant.ghost;
            size: ControlSize.xsmall;
            clicked => {
                root.order-open = !root.order-open;
            }
        }
    }
    Rectangle {
        border-width: 1px;
        border-color: Theme.border;
        border-radius: Theme.radius-lg;
        background: Theme.muted.with-alpha(0.3);
        HorizontalLayout {
            padding-left: 12px;
            padding-right: 12px;
            padding-top: 8px;
            padding-bottom: 8px;
            spacing: 8px;
            alignment: center;
            Text {
                horizontal-stretch: 1;
                text: "Status";
                color: Theme.muted-foreground;
                font-size: Theme.font-size-sm;
                vertical-alignment: center;
            }
            Tag {
                text: "Shipped";
                variant: TagVariant.status;
                status: Status.success;
                size: ControlSize.small;
            }
        }
    }
    Collapsible {
        open <=> root.order-open;
        VerticalLayout {
            spacing: 8px;
            Rectangle {
                border-width: 1px;
                border-color: Theme.border;
                border-radius: Theme.radius-lg;
                VerticalLayout {
                    padding: 8px;
                    padding-left: 12px;
                    padding-right: 12px;
                    Text {
                        text: "Shipping address";
                        color: Theme.foreground;
                        font-size: Theme.font-size-sm;
                        font-weight: 500;
                    }
                    Text {
                        text: "100 Market St, San Francisco";
                        color: Theme.muted-foreground;
                        font-size: Theme.font-size-sm;
                        wrap: word-wrap;
                    }
                }
            }
            Rectangle {
                border-width: 1px;
                border-color: Theme.border;
                border-radius: Theme.radius-lg;
                VerticalLayout {
                    padding: 8px;
                    padding-left: 12px;
                    padding-right: 12px;
                    Text {
                        text: "Items";
                        color: Theme.foreground;
                        font-size: Theme.font-size-sm;
                        font-weight: 500;
                    }
                    Text {
                        text: "2x Studio Headphones";
                        color: Theme.muted-foreground;
                        font-size: Theme.font-size-sm;
                    }
                }
            }
        }
    }
}

#Row trigger

GPUINo GPUI version of this example yet.
SlintNo Slint version of this example yet.
// The built-in trigger is the whole row: the component draws the title and
// the ruled chevron and reports the caller's message.
let row_trigger = Collapsible::new()
    .trigger(collapsible::trigger(
        "How do I reset my password?",
        app.row_open,
        Message::ToggleRow,
    ))
    .open(app.row_open)
    .content(line(
        "Click the Forgot Password link on the sign in page, and we will send \
         you an email with instructions to create a new one.",
        theme::FONT_SIZE_SM,
        muted,
    ));

The whole row is the trigger.

Collapsible::new()
    .open(self.is_open("faq"))
    .child(
        h_flex()
            .id("faq")
            .justify_between()
            .on_click(cx.listener(|this, _, _, cx| this.toggle("faq", cx)))
            .child("How do I reset my password?")
            .child(chevron),
    )
    .content("Click the Forgot Password link on the sign in page.")
Collapsible {
    title: "How do I reset my password?";
    Text {
        text: "Click the Forgot Password link on the sign in page, and we will send you an email with instructions to create a new one.";
        color: Theme.muted-foreground;
        font-size: Theme.font-size-sm;
        wrap: word-wrap;
    }
}

#Bottom trigger

GPUINo GPUI version of this example yet.
SlintNo Slint version of this example yet.
// The trigger sits on the bottom edge of the card it opens, so the panel's
// content is the breakdown above it and the button below reopens it. The
// button's mark is one chevron that turns half a turn to point up as the
// panel arrives, and the panel is animated so the turn and the reveal are
// one motion. The other triggers draw their two end glyphs and swap, which
// is what the Slint example does with theirs.
let usage = Collapsible::new()
    .open(app.usage_open)
    .animated(true)
    .content(
        column![
            row![
                line("Requests", theme::FONT_SIZE_XS, muted).width(Length::Fill),
                line("$210.84", theme::FONT_SIZE_XS, |theme| Tokens::of(theme)
                    .foreground),
            ]
            .spacing(8.0),
            row![
                line("Active CPU", theme::FONT_SIZE_XS, muted).width(Length::Fill),
                line("$21.95", theme::FONT_SIZE_XS, |theme| Tokens::of(theme)
                    .foreground),
            ]
            .spacing(8.0),
            row![
                line("Events", theme::FONT_SIZE_XS, muted).width(Length::Fill),
                line("$21.20", theme::FONT_SIZE_XS, |theme| Tokens::of(theme)
                    .foreground),
            ]
            .spacing(8.0),
            row![
                line("Storage", theme::FONT_SIZE_XS, muted).width(Length::Fill),
                line("$20.45", theme::FONT_SIZE_XS, |theme| Tokens::of(theme)
                    .foreground),
            ]
            .spacing(8.0),
        ]
        .spacing(8.0),
    );

let usage_card = card(
    column![
        row![
            line("3 days remaining in cycle", theme::FONT_SIZE_SM, |theme| {
                Tokens::of(theme).foreground
            })
            .width(Length::Fill),
            Button::new("Billing")
                .outline()
                .size(ControlSize::XSmall)
                .on_press(Message::Notice("Billing")),
        ]
        .spacing(8.0)
        .align_y(Alignment::Center),
        soft_card(
            column![
                row![
                    line("$18.08 / $20", theme::FONT_SIZE_SM, |theme| Tokens::of(
                        theme
                    )
                    .foreground),
                    line("$200", theme::FONT_SIZE_SM, |theme| Tokens::of(theme)
                        .foreground),
                ]
                .spacing(8.0),
                Progress::new(0.9),
            ]
            .spacing(8.0),
        ),
        usage,
    ]
    .spacing(12.0),
);

let bottom = column![
    usage_card,
    container(
        Button::new("")
            .outline()
            .size(ControlSize::XSmall)
            .icon(turning_chevron(app.usage_open))
            .on_press(Message::ToggleUsage),
    )
    .width(Length::Fill)
    .center_x(Length::Fill),
]
.spacing(0.0);

The trigger sits on the bottom edge of the card it opens.

Collapsible::new()
    .open(self.is_open("usage"))
    .child(usage_summary)
    .content(usage_breakdown)
VerticalLayout {
    spacing: 0px;
    Rectangle {
        border-width: 1px;
        border-color: Theme.border;
        border-radius: Theme.radius-lg;
        VerticalLayout {
            padding: 12px;
            spacing: 12px;
            HorizontalLayout {
                spacing: 8px;
                alignment: center;
                Text {
                    horizontal-stretch: 1;
                    text: "3 days remaining in cycle";
                    color: Theme.foreground;
                    font-size: Theme.font-size-sm;
                    vertical-alignment: center;
                }
                Button {
                    text: "Billing";
                    outline: true;
                    size: ControlSize.xsmall;
                }
            }
            Rectangle {
                border-width: 1px;
                border-color: Theme.border;
                border-radius: Theme.radius-lg;
                background: Theme.muted.with-alpha(0.6);
                VerticalLayout {
                    padding: 8px;
                    padding-left: 12px;
                    padding-right: 12px;
                    spacing: 8px;
                    HorizontalLayout {
                        Text {
                            horizontal-stretch: 1;
                            text: "$18.08 / $20";
                            color: Theme.foreground;
                            font-size: Theme.font-size-sm;
                            font-weight: 500;
                        }
                        Text {
                            text: "$200";
                            color: Theme.foreground;
                            font-size: Theme.font-size-sm;
                            font-weight: 500;
                        }
                    }
                    Progress {
                        value: 90;
                    }
                }
            }
            Collapsible {
                open <=> root.usage-open;
                VerticalLayout {
                    spacing: 8px;
                    HorizontalLayout {
                        Text {
                            horizontal-stretch: 1;
                            text: "Requests";
                            color: Theme.muted-foreground;
                            font-size: Theme.font-size-xs;
                            font-weight: 500;
                        }
                        Text {
                            text: "$210.84";
                            color: Theme.foreground;
                            font-size: Theme.font-size-xs;
                            font-weight: 500;
                        }
                    }
                    HorizontalLayout {
                        Text {
                            horizontal-stretch: 1;
                            text: "Active CPU";
                            color: Theme.muted-foreground;
                            font-size: Theme.font-size-xs;
                            font-weight: 500;
                        }
                        Text {
                            text: "$21.95";
                            color: Theme.foreground;
                            font-size: Theme.font-size-xs;
                            font-weight: 500;
                        }
                    }
                    HorizontalLayout {
                        Text {
                            horizontal-stretch: 1;
                            text: "Events";
                            color: Theme.muted-foreground;
                            font-size: Theme.font-size-xs;
                            font-weight: 500;
                        }
                        Text {
                            text: "$21.20";
                            color: Theme.foreground;
                            font-size: Theme.font-size-xs;
                            font-weight: 500;
                        }
                    }
                    HorizontalLayout {
                        Text {
                            horizontal-stretch: 1;
                            text: "Storage";
                            color: Theme.muted-foreground;
                            font-size: Theme.font-size-xs;
                            font-weight: 500;
                        }
                        Text {
                            text: "$20.45";
                            color: Theme.foreground;
                            font-size: Theme.font-size-xs;
                            font-weight: 500;
                        }
                    }
                }
            }
        }
    }
    Rectangle {
        height: 0px;
        horizontal-stretch: 1;
        Button {
            x: (parent.width - self.width) / 2;
            y: -12px;
            icon: root.usage-open ? "chevron-up" : "chevron-down";
            outline: true;
            size: ControlSize.xsmall;
            clicked => {
                root.usage-open = !root.usage-open;
            }
        }
    }
}

#Settings

GPUINo GPUI version of this example yet.
SlintNo Slint version of this example yet.
// The trigger is the caller's own row, which is why the component reports
// only the message: here a chevron and a title open the notification panel.
let settings = column![
    trigger_row(
        app.settings_open,
        "Notification settings",
        Message::ToggleSettings
    ),
    Collapsible::new().open(app.settings_open).content(card(
        column![
            Checkbox::new(app.push)
                .label("Push notifications")
                .on_toggle(Message::Push),
            rule(),
            Checkbox::new(app.email)
                .label("Email notifications")
                .on_toggle(Message::Email),
            rule(),
            Checkbox::new(app.sms)
                .label("SMS notifications")
                .on_toggle(Message::Sms),
        ]
        .spacing(8.0),
    )),
]
.spacing(8.0);

Holds optional controls, keeping the default view short.

Collapsible::new()
    .open(self.is_open("settings"))
    .child(
        Button::new("settings")
            .outline()
            .label("Notification settings")
            .on_click(cx.listener(|this, _, _, cx| this.toggle("settings", cx))),
    )
    .content(notification_checkboxes)
VerticalLayout {
    spacing: 8px;
    Rectangle {
        border-width: 1px;
        border-color: Theme.border;
        border-radius: Theme.radius-md;
        background: settings-touch.has-hover ? Theme.accent : transparent;
        HorizontalLayout {
            padding-left: 10px;
            padding-right: 10px;
            padding-top: 6px;
            padding-bottom: 6px;
            spacing: 8px;
            alignment: center;
            Icon {
                name: root.settings-open ? "chevron-down" : "chevron-right";
                size: 14px;
                color: Theme.muted-foreground;
            }
            Text {
                text: "Notification settings";
                color: Theme.foreground;
                font-size: Theme.font-size-sm;
                font-weight: 500;
                vertical-alignment: center;
            }
        }
        settings-touch := TouchArea {
            clicked => {
                root.settings-open = !root.settings-open;
            }
        }
    }
    Collapsible {
        open <=> root.settings-open;
        Rectangle {
            border-width: 1px;
            border-color: Theme.border;
            border-radius: Theme.radius-lg;
            VerticalLayout {
                padding: 8px;
                padding-left: 12px;
                padding-right: 12px;
                spacing: 8px;
                Checkbox {
                    label: "Push notifications";
                    checked <=> root.push;
                }
                Rectangle {
                    height: 1px;
                    background: Theme.border;
                }
                Checkbox {
                    label: "Email notifications";
                    checked <=> root.email;
                }
                Rectangle {
                    height: 1px;
                    background: Theme.border;
                }
                Checkbox {
                    label: "SMS notifications";
                    checked <=> root.sms;
                }
            }
        }
    }
}

#Row actions

GPUINo GPUI version of this example yet.
SlintNo Slint version of this example yet.
// Actions live beside the trigger and in every row: the plus adds a key, the
// ellipsis holds each row's own commands.
let keys = column![
    row![
        trigger_row(app.keys_open, "API Keys", Message::ToggleKeys),
        icon_button(PLUS, Message::Notice("Add key")),
    ]
    .spacing(8.0)
    .align_y(Alignment::Center),
    Collapsible::new().open(app.keys_open).content(
        column![
            key_row("Production", "PRDK230454*242SDIFPPL"),
            key_row("Development", "DUILO30454*242SDIFUIP"),
            key_row("Staging", "IPPODAS230454*242SDI"),
        ]
        .spacing(8.0),
    ),
]
.spacing(12.0);

Actions live beside the trigger, in the header and in every row.

Collapsible::new()
    .open(self.is_open("api-keys"))
    .child(
        h_flex()
            .child(api_keys_trigger)
            .child(Button::new("add-key").ghost().xsmall().icon(IconName::Plus)),
    )
    .content(api_key_rows)
VerticalLayout {
    spacing: 12px;
    HorizontalLayout {
        spacing: 8px;
        alignment: center;
        Rectangle {
            horizontal-stretch: 1;
            background: keys-touch.has-hover ? Theme.accent : transparent;
            border-radius: Theme.radius-sm;
            HorizontalLayout {
                padding-top: 4px;
                padding-bottom: 4px;
                spacing: 8px;
                alignment: center;
                Icon {
                    name: root.keys-open ? "chevron-down" : "chevron-right";
                    size: 14px;
                    color: Theme.muted-foreground;
                }
                Text {
                    text: "API Keys";
                    color: Theme.foreground;
                    font-size: Theme.font-size-sm;
                    font-weight: 500;
                    vertical-alignment: center;
                }
            }
            keys-touch := TouchArea {
                clicked => {
                    root.keys-open = !root.keys-open;
                }
            }
        }
        Button {
            icon: "plus";
            variant: ButtonVariant.ghost;
            size: ControlSize.xsmall;
        }
    }
    Collapsible {
        open <=> root.keys-open;
        VerticalLayout {
            spacing: 8px;
            HorizontalLayout {
                spacing: 8px;
                alignment: center;
                Rectangle {
                    width: 20px;
                    height: 20px;
                    border-radius: Theme.radius-sm;
                    background: Theme.muted;
                    Icon {
                        x: (parent.width - self.width) / 2;
                        y: (parent.height - self.height) / 2;
                        name: "hash";
                        size: 12px;
                        color: Theme.chart-2;
                    }
                }
                Text {
                    text: "Production";
                    color: Theme.foreground;
                    font-size: Theme.font-size-xs;
                }
                Rectangle {
                    horizontal-stretch: 1;
                    border-radius: Theme.radius-sm;
                    background: Theme.muted;
                    Text {
                        text: "PRDK230454*242SDIFPPL";
                        color: Theme.foreground;
                        font-size: Theme.font-size-xs;
                        font-family: Theme.mono-font-family;
                        vertical-alignment: center;
                        overflow: elide;
                    }
                }
                Button {
                    icon: "ellipsis";
                    variant: ButtonVariant.ghost;
                    size: ControlSize.xsmall;
                }
            }
            HorizontalLayout {
                spacing: 8px;
                alignment: center;
                Rectangle {
                    width: 20px;
                    height: 20px;
                    border-radius: Theme.radius-sm;
                    background: Theme.muted;
                    Icon {
                        x: (parent.width - self.width) / 2;
                        y: (parent.height - self.height) / 2;
                        name: "hash";
                        size: 12px;
                        color: Theme.chart-2;
                    }
                }
                Text {
                    text: "Development";
                    color: Theme.foreground;
                    font-size: Theme.font-size-xs;
                }
                Rectangle {
                    horizontal-stretch: 1;
                    border-radius: Theme.radius-sm;
                    background: Theme.muted;
                    Text {
                        text: "DUILO30454*242SDIFUIP";
                        color: Theme.foreground;
                        font-size: Theme.font-size-xs;
                        font-family: Theme.mono-font-family;
                        vertical-alignment: center;
                        overflow: elide;
                    }
                }
                Button {
                    icon: "ellipsis";
                    variant: ButtonVariant.ghost;
                    size: ControlSize.xsmall;
                }
            }
            HorizontalLayout {
                spacing: 8px;
                alignment: center;
                Rectangle {
                    width: 20px;
                    height: 20px;
                    border-radius: Theme.radius-sm;
                    background: Theme.muted;
                    Icon {
                        x: (parent.width - self.width) / 2;
                        y: (parent.height - self.height) / 2;
                        name: "hash";
                        size: 12px;
                        color: Theme.chart-2;
                    }
                }
                Text {
                    text: "Staging";
                    color: Theme.foreground;
                    font-size: Theme.font-size-xs;
                }
                Rectangle {
                    horizontal-stretch: 1;
                    border-radius: Theme.radius-sm;
                    background: Theme.muted;
                    Text {
                        text: "IPPODAS230454*242SDI";
                        color: Theme.foreground;
                        font-size: Theme.font-size-xs;
                        font-family: Theme.mono-font-family;
                        vertical-alignment: center;
                        overflow: elide;
                    }
                }
                Button {
                    icon: "ellipsis";
                    variant: ButtonVariant.ghost;
                    size: ControlSize.xsmall;
                }
            }
        }
    }
}

#Nested

GPUINo GPUI version of this example yet.
SlintNo Slint version of this example yet.
// Panels nest to any depth: the folder rows are triggers, and the indent is
// the caller's, so a nested panel returns to its parent's spine when it ends.
let nested = column![
    folder_row(
        app.components_open,
        "components",
        8.0,
        Message::ToggleComponents
    ),
    Collapsible::new().open(app.components_open).content(
        column![
            folder_row(app.ui_open, "ui", 20.0, Message::ToggleUi),
            Collapsible::new().open(app.ui_open).content(
                column![
                    file_row("button.rs", 48.0),
                    file_row("card.rs", 48.0),
                    file_row("dialog.rs", 48.0),
                ]
                .spacing(2.0),
            ),
            file_row("login_form.rs", 32.0),
        ]
        .spacing(2.0),
    ),
    file_row("main.rs", 22.0),
]
.spacing(2.0);

Panels nest to any depth.

Collapsible::new()
    .open(self.is_open("components-dir"))
    .child(folder_row("components"))
    .content(
        Collapsible::new()
            .open(self.is_open("ui-dir"))
            .child(folder_row("ui"))
            .content(v_flex().children(["button.rs", "card.rs", "dialog.rs"])),
    )
VerticalLayout {
    Rectangle {
        border-radius: Theme.radius-sm;
        background: components-touch.has-hover ? Theme.accent : transparent;
        HorizontalLayout {
            padding: 4px;
            padding-left: 8px;
            padding-right: 8px;
            spacing: 8px;
            alignment: center;
            Icon {
                name: root.components-open ? "chevron-down" : "chevron-right";
                size: 14px;
                color: Theme.muted-foreground;
            }
            Icon {
                name: root.components-open ? "folder-open" : "folder";
                size: 14px;
                color: Theme.muted-foreground;
            }
            Text {
                text: "components";
                color: Theme.foreground;
                font-size: Theme.font-size-sm;
                vertical-alignment: center;
            }
        }
        components-touch := TouchArea {
            clicked => {
                root.components-open = !root.components-open;
            }
        }
    }
    Collapsible {
        open <=> root.components-open;
        VerticalLayout {
            Rectangle {
                border-radius: Theme.radius-sm;
                background: ui-touch.has-hover ? Theme.accent : transparent;
                HorizontalLayout {
                    padding: 4px;
                    padding-left: 20px;
                    padding-right: 8px;
                    spacing: 8px;
                    alignment: center;
                    Icon {
                        name: root.ui-open ? "chevron-down" : "chevron-right";
                        size: 14px;
                        color: Theme.muted-foreground;
                    }
                    Icon {
                        name: root.ui-open ? "folder-open" : "folder";
                        size: 14px;
                        color: Theme.muted-foreground;
                    }
                    Text {
                        text: "ui";
                        color: Theme.foreground;
                        font-size: Theme.font-size-sm;
                        vertical-alignment: center;
                    }
                }
                ui-touch := TouchArea {
                    clicked => {
                        root.ui-open = !root.ui-open;
                    }
                }
            }
            Collapsible {
                open <=> root.ui-open;
                VerticalLayout {
                    spacing: 2px;
                    HorizontalLayout {
                        padding-left: 48px;
                        spacing: 8px;
                        alignment: center;
                        Icon {
                            name: "file";
                            size: 14px;
                            color: Theme.muted-foreground;
                        }
                        Text {
                            text: "button.rs";
                            color: Theme.foreground;
                            font-size: Theme.font-size-sm;
                        }
                    }
                    HorizontalLayout {
                        padding-left: 48px;
                        spacing: 8px;
                        alignment: center;
                        Icon {
                            name: "file";
                            size: 14px;
                            color: Theme.muted-foreground;
                        }
                        Text {
                            text: "card.rs";
                            color: Theme.foreground;
                            font-size: Theme.font-size-sm;
                        }
                    }
                    HorizontalLayout {
                        padding-left: 48px;
                        spacing: 8px;
                        alignment: center;
                        Icon {
                            name: "file";
                            size: 14px;
                            color: Theme.muted-foreground;
                        }
                        Text {
                            text: "dialog.rs";
                            color: Theme.foreground;
                            font-size: Theme.font-size-sm;
                        }
                    }
                }
            }
            HorizontalLayout {
                padding-left: 32px;
                spacing: 8px;
                alignment: center;
                Icon {
                    name: "file";
                    size: 14px;
                    color: Theme.muted-foreground;
                }
                Text {
                    text: "login_form.rs";
                    color: Theme.foreground;
                    font-size: Theme.font-size-sm;
                }
            }
        }
    }
    HorizontalLayout {
        padding-left: 22px;
        spacing: 8px;
        alignment: center;
        Icon {
            name: "file";
            size: 14px;
            color: Theme.muted-foreground;
        }
        Text {
            text: "main.rs";
            color: Theme.foreground;
            font-size: Theme.font-size-sm;
        }
    }
}

#Profile

GPUINo GPUI version of this example yet.
SlintNo Slint version of this example yet.
// Shows who someone is, and their details only on request.
let profile = column![
    pressable(
        container(
            row![
                Avatar::new().initials("JL").size(AvatarSize::XSmall),
                line("@huacnlee", theme::FONT_SIZE_SM, |theme| Tokens::of(theme)
                    .foreground)
                .width(Length::Fill),
                chevron(app.profile_open),
            ]
            .spacing(8.0)
            .align_y(Alignment::Center),
        )
        .padding([4.0, 8.0])
        .width(Length::Fill),
        Message::ToggleProfile,
    ),
    Collapsible::new().open(app.profile_open).content(
        column![
            detail_line(INBOX, "Last activity", "2 hours ago"),
            detail_line(CALENDAR, "Online since", "Today, 9:00 AM"),
            detail_line(GLOBE, "Location", "Hong Kong"),
        ]
        .spacing(8.0),
    ),
]
.spacing(12.0);

Shows who someone is, and their details only on request.

Collapsible::new()
    .open(self.is_open("profile"))
    .child(
        h_flex()
            .child(Avatar::new().name("Jason Lee").xsmall())
            .child("@huacnlee")
            .child(chevron),
    )
    .content(profile_fields)
VerticalLayout {
    spacing: 12px;
    Rectangle {
        border-radius: Theme.radius-sm;
        background: profile-touch.has-hover ? Theme.accent : transparent;
        HorizontalLayout {
            padding: 4px;
            spacing: 8px;
            alignment: center;
            Avatar {
                initials: "JL";
                size: AvatarSize.xsmall;
            }
            Text {
                horizontal-stretch: 1;
                text: "@huacnlee";
                color: Theme.foreground;
                font-size: Theme.font-size-sm;
                font-weight: 500;
                vertical-alignment: center;
            }
            Icon {
                name: root.profile-open ? "chevron-down" : "chevron-right";
                size: 14px;
                color: Theme.muted-foreground;
            }
        }
        profile-touch := TouchArea {
            clicked => {
                root.profile-open = !root.profile-open;
            }
        }
    }
    Collapsible {
        open <=> root.profile-open;
        VerticalLayout {
            spacing: 8px;
            HorizontalLayout {
                spacing: 8px;
                alignment: center;
                Icon {
                    name: "inbox";
                    size: 14px;
                    color: Theme.muted-foreground;
                }
                Text {
                    text: "Last activity";
                    color: Theme.muted-foreground;
                    font-size: Theme.font-size-xs;
                }
                Text {
                    text: "2 hours ago";
                    color: Theme.foreground;
                    font-size: Theme.font-size-xs;
                    font-weight: 500;
                }
            }
            HorizontalLayout {
                spacing: 8px;
                alignment: center;
                Icon {
                    name: "calendar";
                    size: 14px;
                    color: Theme.muted-foreground;
                }
                Text {
                    text: "Online since";
                    color: Theme.muted-foreground;
                    font-size: Theme.font-size-xs;
                }
                Text {
                    text: "Today, 9:00 AM";
                    color: Theme.foreground;
                    font-size: Theme.font-size-xs;
                    font-weight: 500;
                }
            }
            HorizontalLayout {
                spacing: 8px;
                alignment: center;
                Icon {
                    name: "globe";
                    size: 14px;
                    color: Theme.muted-foreground;
                }
                Text {
                    text: "Location";
                    color: Theme.muted-foreground;
                    font-size: Theme.font-size-xs;
                }
                Text {
                    text: "Hong Kong";
                    color: Theme.foreground;
                    font-size: Theme.font-size-xs;
                    font-weight: 500;
                }
            }
        }
    }
}