Skip to main content

kitchen_ui/widgets/
section.rs

1//! The titled section.
2
3use gpui_kit::component::{h_flex, v_flex};
4use gpui_kit::prelude::FluentBuilder as _;
5use gpui_kit::{AnyElement, App, FontWeight, IntoElement, ParentElement, RenderOnce, SharedString, Styled as _, Window, div, px};
6
7use crate::theme::{self, color};
8
9/// A titled block of a panel: a short caps title (with something at its right, if given), then
10/// its rows (added as children), [`theme::SM`] apart. Boxed ([`Section::boxed`]), the rows sit in
11/// a bordered box.
12#[derive(IntoElement)]
13pub struct Section {
14    title: SharedString,
15    extra: Option<AnyElement>,
16    boxed: bool,
17    children: Vec<AnyElement>,
18}
19
20impl Section {
21    /// A section titled `title` (shown in capitals: a short label, designparser
22    /// `all-caps-fatigue`).
23    pub fn new(title: impl Into<SharedString>) -> Self {
24        Self { title: title.into(), extra: None, boxed: false, children: Vec::new() }
25    }
26
27    /// Something at the right of the title (a count, a button).
28    pub fn extra(mut self, e: impl IntoElement) -> Self {
29        self.extra = Some(e.into_any_element());
30        self
31    }
32
33    /// The rows in a bordered box (dsp-kitchen's sections): padding [`theme::MD`], 1.5 × the row
34    /// gap (designparser `padding-gap-ratio`).
35    pub fn boxed(mut self) -> Self {
36        self.boxed = true;
37        self
38    }
39}
40
41impl ParentElement for Section {
42    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
43        self.children.extend(elements);
44    }
45}
46
47impl RenderOnce for Section {
48    fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement {
49        let Self { title, extra, boxed, children } = self;
50        let head = h_flex()
51            .w_full()
52            .min_h(px(20.))
53            .gap(px(theme::SM))
54            .child(div().flex_1().min_w_0().truncate().text_xs().font_weight(FontWeight::SEMIBOLD).text_color(color::text_muted()).child(title.to_uppercase()))
55            .children(extra);
56        let body = v_flex().w_full().min_w_0().gap(px(theme::SM)).children(children).when(boxed, |d| {
57            d.p(px(theme::MD)).rounded(px(theme::RADIUS_LG)).border_1().border_color(color::border()).bg(color::raised())
58        });
59        v_flex().w_full().min_w_0().gap(px(theme::SM)).child(head).child(body)
60    }
61}