use gpui::{AnyElement, ClickEvent, prelude::*}; use crate::{ButtonLike, CommonAnimationExt, Tooltip, prelude::*}; /// A button component displayed in the title bar to show auto-update status. #[derive(IntoElement, RegisterComponent)] pub struct UpdateButton { icon: IconName, icon_animate: bool, icon_color: Option, message: SharedString, tooltip: Option, show_dismiss: bool, on_click: Option>, on_dismiss: Option>, } impl UpdateButton { pub fn new(icon: IconName, message: impl Into) -> Self { Self { icon, icon_animate: false, icon_color: None, message: message.into(), tooltip: None, show_dismiss: false, on_click: None, on_dismiss: None, } } /// Sets whether the icon should have a rotation animation (for progress states). pub fn icon_animate(mut self, animate: bool) -> Self { self.icon_animate = animate; self } /// Sets the icon color (e.g., for warning/error states). pub fn icon_color(mut self, color: impl Into>) -> Self { self.icon_color = color.into(); self } /// Sets the tooltip text shown on hover. pub fn tooltip(mut self, tooltip: impl Into) -> Self { self.tooltip = Some(tooltip.into()); self } /// Shows a dismiss button on the right side. pub fn with_dismiss(mut self) -> Self { self.show_dismiss = true; self } /// Sets the click handler for the main button area. pub fn on_click( mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, ) -> Self { self.on_click = Some(Box::new(handler)); self } /// Sets the click handler for the dismiss button. pub fn on_dismiss( mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, ) -> Self { self.on_dismiss = Some(Box::new(handler)); self } pub fn checking() -> Self { Self::new(IconName::ArrowCircle, "Checking for Zed updates…").icon_animate(true) } pub fn downloading(version: impl Into) -> Self { Self::new(IconName::Download, "Downloading Zed update…").tooltip(version) } pub fn installing(version: impl Into) -> Self { Self::new(IconName::ArrowCircle, "Installing Zed update…") .icon_animate(true) .tooltip(version) } pub fn updated(version: impl Into) -> Self { Self::new(IconName::Download, "Restart to Update") .tooltip(version) .with_dismiss() } pub fn errored(error: impl Into) -> Self { Self::new(IconName::Warning, "Failed to update Zed") .icon_color(Color::Warning) .tooltip(error) .with_dismiss() } } impl RenderOnce for UpdateButton { fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement { let border_color = cx.theme().colors().border; let icon = Icon::new(self.icon) .size(IconSize::XSmall) .when_some(self.icon_color, |this, color| this.color(color)); let icon_element = if self.icon_animate { icon.with_rotate_animation(3).into_any_element() } else { icon.into_any_element() }; let tooltip = self.tooltip.clone(); h_flex() .mr_2() .rounded_sm() .border_1() .border_color(border_color) .child( ButtonLike::new("update-button") .child( h_flex() .h_full() .gap_1() .child(icon_element) .child(Label::new(self.message).size(LabelSize::Small)), ) .when_some(tooltip, |this, tooltip| { this.tooltip(Tooltip::text(tooltip)) }) .when_some(self.on_click, |this, handler| this.on_click(handler)), ) .when(self.show_dismiss, |this| { this.child( div().border_l_1().border_color(border_color).child( IconButton::new("dismiss-update-button", IconName::Close) .icon_size(IconSize::Indicator) .when_some(self.on_dismiss, |this, handler| this.on_click(handler)) .tooltip(Tooltip::text("Dismiss")), ), ) }) } } impl Component for UpdateButton { fn scope() -> ComponentScope { ComponentScope::Collaboration } fn name() -> &'static str { "UpdateButton" } fn description() -> Option<&'static str> { Some( "A button component displayed in the title bar to show auto-update status and allow users to restart Zed.", ) } fn preview(_window: &mut Window, _cx: &mut App) -> Option { let version = "1.99.0"; Some( v_flex() .gap_6() .children(vec![ example_group_with_title( "Progress States", vec![ single_example("Checking", UpdateButton::checking().into_any_element()), single_example( "Downloading", UpdateButton::downloading(version).into_any_element(), ), single_example( "Installing", UpdateButton::installing(version).into_any_element(), ), ], ), example_group_with_title( "Actionable States", vec![ single_example( "Ready to Update", UpdateButton::updated(version).into_any_element(), ), single_example( "Error", UpdateButton::errored("Network timeout").into_any_element(), ), ], ), ]) .into_any_element(), ) } }