content_group.rs

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