guise-ui 1.4.0

A component library for gpui, Zed's GPU-accelerated UI framework: a themed palette, sizing tokens, 130+ composable components, a reactive state layer, and an in-app Safari-style inspector.
Documentation
//! `SettingsSection` — a titled group of rows inside a settings page.
//!
//! A page is rarely one flat list. "Appearance" wants Theme separated from
//! Typography; "Security" wants the settings worth reading twice under their
//! own heading. This is that grouping: a title, an optional sentence, a rule,
//! and the rows.
//!
//! It is a plain [`ParentElement`], so the rows are whatever the caller puts
//! in — usually [`SettingsRow`](super::SettingsRow), but a section holding a
//! chart or a table is just as valid.

use gpui::prelude::*;
use gpui::{div, px, AnyElement, App, FontWeight, IntoElement, SharedString, Window};

use crate::devtools::Probed;
use crate::theme::{theme, Size};

/// A titled group of settings.
#[derive(IntoElement)]
pub struct SettingsSection {
    title: SharedString,
    description: Option<SharedString>,
    rule: bool,
    children: Vec<AnyElement>,
}

impl SettingsSection {
    pub fn new(title: impl Into<SharedString>) -> Self {
        SettingsSection {
            title: title.into(),
            description: None,
            rule: true,
            children: Vec::new(),
        }
    }

    /// A sentence under the heading.
    pub fn description(mut self, description: impl Into<SharedString>) -> Self {
        self.description = Some(description.into());
        self
    }

    /// Draw the rule under the heading (default `true`).
    pub fn rule(mut self, rule: bool) -> Self {
        self.rule = rule;
        self
    }
}

impl ParentElement for SettingsSection {
    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
        self.children.extend(elements);
    }
}

impl RenderOnce for SettingsSection {
    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
        let t = theme(cx);
        let text = t.text().hsla();
        let dimmed = t.dimmed().hsla();
        let border = t.border().hsla();
        let font_md = t.font_size(Size::Md);
        let font_sm = t.font_size(Size::Sm);
        let gap = t.spacing(Size::Xs);

        let mut header = div().flex().flex_col().w_full().gap(px(gap)).child(
            div()
                .text_size(px(font_md))
                .font_weight(FontWeight::SEMIBOLD)
                .text_color(text)
                .child(self.title.clone()),
        );
        if let Some(description) = self.description {
            header = header.child(
                div()
                    .text_size(px(font_sm))
                    .text_color(dimmed)
                    .child(description),
            );
        }
        if self.rule {
            header = header.child(div().w_full().h(px(1.0)).bg(border));
        }

        div()
            .flex()
            .flex_col()
            .w_full()
            .pt(px(18.0))
            .gap(px(gap))
            .child(header)
            .children(self.children)
            .probe("SettingsSection")
            .attr("title", self.title)
    }
}