use gpui::prelude::FluentBuilder as _; use gpui::{ App, IntoElement, ParentElement as _, RenderOnce, SharedString, StyleRefinement, Styled as _, Window, div, relative, }; use gpui_native_icons::Icon; use gpui_native_theme::{ActiveTheme as _, Size}; use crate::common::{StyledExt, v_flex}; /// The centered "nothing here yet" panel for lists, tables, and searches. #[derive(IntoElement)] #[must_use] pub struct EmptyState { icon: Option, headline: SharedString, hint: Option, action: Option, style: StyleRefinement, } impl EmptyState { /// Creates an empty state whose headline is the primary statement. pub fn new(headline: L) -> Self where L: Into, { Self { icon: None, headline: headline.into(), hint: None, action: None, style: StyleRefinement::default(), } } /// Sets a large muted icon above the headline. pub fn icon(mut self, icon: I) -> Self where I: Into, { self.icon = Some(icon.into()); self } /// Sets a secondary line under the headline. pub fn hint(mut self, hint: L) -> Self where L: Into, { self.hint = Some(hint.into()); self } /// Sets an action element — usually a button — under the hint. pub fn action(mut self, action: E) -> Self where E: IntoElement, { self.action = Some(action.into_any_element()); self } } impl gpui::Styled for EmptyState { fn style(&mut self) -> &mut StyleRefinement { &mut self.style } } impl RenderOnce for EmptyState { fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement { let theme = cx.theme(); let metrics = theme.sizes.resolve(Size::Medium); let muted_foreground = theme.colors.muted_foreground; let foreground = theme.colors.foreground; let illustration = metrics.icon_size * 2.0; v_flex() .w_full() .items_center() .justify_center() .text_center() .p_4() .when_some(self.icon, |state, icon| { state.child( div() .mb_2() .child(icon.size(illustration).color(muted_foreground)), ) }) .child( div() .text_size(metrics.text_size) .font_weight(gpui::FontWeight::MEDIUM) .text_color(foreground) .line_height(relative(1.3)) .child(self.headline), ) .when_some(self.hint, |state, hint| { state.child( div() .text_size(metrics.caption_size) .text_color(muted_foreground) .child(hint), ) }) .when_some(self.action, |state, action| { state.child(div().mt_3().child(action)) }) .refine_style(&self.style) } }