content_group.rs

  1use crate::prelude::*;
  2use gpui::{AnyElement, IntoElement, ParentElement, StyleRefinement, Styled};
  3use smallvec::SmallVec;
  4
  5/// Creates a new [ContentGroup].
  6pub fn content_group() -> ContentGroup {
  7    ContentGroup::new()
  8}
  9
 10/// A [ContentGroup] that vertically stacks its children.
 11///
 12/// This is a convenience function that simply combines [`ContentGroup`] and [`v_flex`](crate::v_flex).
 13pub fn v_group() -> ContentGroup {
 14    content_group().v_flex()
 15}
 16
 17/// Creates a new horizontal [ContentGroup].
 18///
 19/// This is a convenience function that simply combines [`ContentGroup`] and [`h_flex`](crate::h_flex).
 20pub fn h_group() -> ContentGroup {
 21    content_group().h_flex()
 22}
 23
 24/// A flexible container component that can hold other elements.
 25#[derive(IntoElement)]
 26pub struct ContentGroup {
 27    base: Div,
 28    border: bool,
 29    fill: bool,
 30    children: SmallVec<[AnyElement; 2]>,
 31}
 32
 33impl ContentGroup {
 34    /// Creates a new [ContentBox].
 35    pub fn new() -> Self {
 36        Self {
 37            base: div(),
 38            border: true,
 39            fill: true,
 40            children: SmallVec::new(),
 41        }
 42    }
 43
 44    /// Removes the border from the [ContentBox].
 45    pub fn borderless(mut self) -> Self {
 46        self.border = false;
 47        self
 48    }
 49
 50    /// Removes the background fill from the [ContentBox].
 51    pub fn unfilled(mut self) -> Self {
 52        self.fill = false;
 53        self
 54    }
 55}
 56
 57impl ParentElement for ContentGroup {
 58    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
 59        self.children.extend(elements)
 60    }
 61}
 62
 63impl Styled for ContentGroup {
 64    fn style(&mut self) -> &mut StyleRefinement {
 65        self.base.style()
 66    }
 67}
 68
 69impl RenderOnce for ContentGroup {
 70    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
 71        // TODO:
 72        // Baked in padding will make scrollable views inside of content boxes awkward.
 73        //
 74        // Do we make the padding optional, or do we push to use a different component?
 75
 76        self.base
 77            .when(self.fill, |this| {
 78                this.bg(cx.theme().colors().text.opacity(0.05))
 79            })
 80            .when(self.border, |this| {
 81                this.border_1().border_color(cx.theme().colors().border)
 82            })
 83            .rounded_md()
 84            .p_2()
 85            .children(self.children)
 86    }
 87}
 88
 89impl ComponentPreview for ContentGroup {
 90    fn description() -> impl Into<Option<&'static str>> {
 91        "A flexible container component that can hold other elements. It can be customized with or without a border and background fill."
 92    }
 93
 94    fn example_label_side() -> ExampleLabelSide {
 95        ExampleLabelSide::Bottom
 96    }
 97
 98    fn examples(_window: &mut Window, _: &mut App) -> Vec<ComponentExampleGroup<Self>> {
 99        vec![example_group(vec![
100            single_example(
101                "Default",
102                ContentGroup::new()
103                    .flex_1()
104                    .items_center()
105                    .justify_center()
106                    .h_48()
107                    .child(Label::new("Default ContentBox")),
108            )
109            .grow(),
110            single_example(
111                "Without Border",
112                ContentGroup::new()
113                    .flex_1()
114                    .items_center()
115                    .justify_center()
116                    .h_48()
117                    .borderless()
118                    .child(Label::new("Borderless ContentBox")),
119            )
120            .grow(),
121            single_example(
122                "Without Fill",
123                ContentGroup::new()
124                    .flex_1()
125                    .items_center()
126                    .justify_center()
127                    .h_48()
128                    .unfilled()
129                    .child(Label::new("Unfilled ContentBox")),
130            )
131            .grow(),
132        ])
133        .grow()]
134    }
135}