diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 24ec46ca61..b5ef81bfba 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -5549,7 +5549,7 @@ impl Editor { })) .child( h_flex() - .w_full() + .flex_1() .gap_2() .child(Icon::new(IconName::ZedPredict)) .child(Label::new("Accept Terms of Service")) diff --git a/crates/inline_completion_button/src/inline_completion_button.rs b/crates/inline_completion_button/src/inline_completion_button.rs index a28cfe99c1..1864e0c266 100644 --- a/crates/inline_completion_button/src/inline_completion_button.rs +++ b/crates/inline_completion_button/src/inline_completion_button.rs @@ -24,8 +24,8 @@ use std::{ }; use supermaven::{AccountStatus, Supermaven}; use ui::{ - prelude::*, Clickable, ContextMenu, ContextMenuEntry, IconButton, IconButtonShape, PopoverMenu, - PopoverMenuHandle, Tooltip, + prelude::*, Clickable, ContextMenu, ContextMenuEntry, IconButton, IconButtonShape, Indicator, + PopoverMenu, PopoverMenuHandle, Tooltip, }; use workspace::{ create_and_open_local_file, item::ItemHandle, notifications::NotificationId, StatusItemView, @@ -240,24 +240,20 @@ impl Render for InlineCompletionButton { let current_user_terms_accepted = self.user_store.read(cx).current_user_has_accepted_terms(); - let icon_button = || { - let base = IconButton::new("zed-predict-pending-button", zeta_icon) - .shape(IconButtonShape::Square); + if !current_user_terms_accepted.unwrap_or(false) { + let signed_in = current_user_terms_accepted.is_some(); + let tooltip_meta = if signed_in { + "Read Terms of Service" + } else { + "Sign in to use" + }; - match ( - current_user_terms_accepted, - self.popover_menu_handle.is_deployed(), - enabled, - ) { - (Some(false) | None, _, _) => { - let signed_in = current_user_terms_accepted.is_some(); - let tooltip_meta = if signed_in { - "Read Terms of Service" - } else { - "Sign in to use" - }; - - base.tooltip(move |window, cx| { + return div().child( + IconButton::new("zed-predict-pending-button", zeta_icon) + .shape(IconButtonShape::Square) + .indicator(Indicator::dot().color(Color::Error)) + .indicator_border_color(Some(cx.theme().colors().status_bar_background)) + .tooltip(move |window, cx| { Tooltip::with_meta( "Edit Predictions", None, @@ -266,34 +262,38 @@ impl Render for InlineCompletionButton { cx, ) }) - .on_click(cx.listener( - move |_, _, window, cx| { - telemetry::event!( - "Pending ToS Clicked", - source = "Edit Prediction Status Button" - ); - window.dispatch_action( - zed_actions::OpenZedPredictOnboarding.boxed_clone(), - cx, - ); - }, - )) + .on_click(cx.listener(move |_, _, window, cx| { + telemetry::event!( + "Pending ToS Clicked", + source = "Edit Prediction Status Button" + ); + window.dispatch_action( + zed_actions::OpenZedPredictOnboarding.boxed_clone(), + cx, + ); + })), + ); + } + + let icon_button = IconButton::new("zed-predict-pending-button", zeta_icon) + .shape(IconButtonShape::Square) + .when(!self.popover_menu_handle.is_deployed(), |element| { + if enabled { + element.tooltip(|window, cx| { + Tooltip::for_action("Edit Prediction", &ToggleMenu, window, cx) + }) + } else { + element.tooltip(|window, cx| { + Tooltip::with_meta( + "Edit Prediction", + Some(&ToggleMenu), + "Disabled For This File", + window, + cx, + ) + }) } - (Some(true), true, _) => base, - (Some(true), false, true) => base.tooltip(|window, cx| { - Tooltip::for_action("Edit Prediction", &ToggleMenu, window, cx) - }), - (Some(true), false, false) => base.tooltip(|window, cx| { - Tooltip::with_meta( - "Edit Prediction", - Some(&ToggleMenu), - "Disabled For This File", - window, - cx, - ) - }), - } - }; + }); let this = cx.entity().clone(); @@ -311,7 +311,7 @@ impl Render for InlineCompletionButton { if is_refreshing { popover_menu = popover_menu.trigger( - icon_button().with_animation( + icon_button.with_animation( "pulsating-label", Animation::new(Duration::from_secs(2)) .repeat() @@ -320,7 +320,7 @@ impl Render for InlineCompletionButton { ), ); } else { - popover_menu = popover_menu.trigger(icon_button()); + popover_menu = popover_menu.trigger(icon_button); } div().child(popover_menu.into_any_element()) diff --git a/crates/ui/src/components/button/button_icon.rs b/crates/ui/src/components/button/button_icon.rs index a2a146ee76..adacd12f27 100644 --- a/crates/ui/src/components/button/button_icon.rs +++ b/crates/ui/src/components/button/button_icon.rs @@ -1,5 +1,6 @@ #![allow(missing_docs)] -use crate::{prelude::*, Icon, IconName, IconSize}; +use crate::{prelude::*, Icon, IconName, IconSize, IconWithIndicator, Indicator}; +use gpui::Hsla; /// An icon that appears within a button. /// @@ -15,6 +16,8 @@ pub(super) struct ButtonIcon { selected_icon: Option, selected_icon_color: Option, selected_style: Option, + indicator: Option, + indicator_border_color: Option, } impl ButtonIcon { @@ -28,6 +31,8 @@ impl ButtonIcon { selected_icon: None, selected_icon_color: None, selected_style: None, + indicator: None, + indicator_border_color: None, } } @@ -56,6 +61,16 @@ impl ButtonIcon { self.selected_icon_color = color.into(); self } + + pub fn indicator(mut self, indicator: Indicator) -> Self { + self.indicator = Some(indicator); + self + } + + pub fn indicator_border_color(mut self, color: Option) -> Self { + self.indicator_border_color = color; + self + } } impl Disableable for ButtonIcon { @@ -96,6 +111,13 @@ impl RenderOnce for ButtonIcon { self.color }; - Icon::new(icon).size(self.size).color(icon_color) + let icon = Icon::new(icon).size(self.size).color(icon_color); + + match self.indicator { + Some(indicator) => IconWithIndicator::new(icon, Some(indicator)) + .indicator_border_color(self.indicator_border_color) + .into_any_element(), + None => icon.into_any_element(), + } } } diff --git a/crates/ui/src/components/button/icon_button.rs b/crates/ui/src/components/button/icon_button.rs index 840f0fc394..c28c5ae9ac 100644 --- a/crates/ui/src/components/button/icon_button.rs +++ b/crates/ui/src/components/button/icon_button.rs @@ -1,8 +1,8 @@ #![allow(missing_docs)] -use gpui::{AnyView, DefiniteLength}; +use gpui::{AnyView, DefiniteLength, Hsla}; use super::button_like::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle}; -use crate::{prelude::*, ElevationIndex, SelectableButton}; +use crate::{prelude::*, ElevationIndex, Indicator, SelectableButton}; use crate::{IconName, IconSize}; use super::button_icon::ButtonIcon; @@ -22,6 +22,8 @@ pub struct IconButton { icon_size: IconSize, icon_color: Color, selected_icon: Option, + indicator: Option, + indicator_border_color: Option, alpha: Option, } @@ -34,6 +36,8 @@ impl IconButton { icon_size: IconSize::default(), icon_color: Color::Default, selected_icon: None, + indicator: None, + indicator_border_color: None, alpha: None, }; this.base.base = this.base.base.debug_selector(|| format!("ICON-{:?}", icon)); @@ -64,6 +68,16 @@ impl IconButton { self.selected_icon = icon.into(); self } + + pub fn indicator(mut self, indicator: Indicator) -> Self { + self.indicator = Some(indicator); + self + } + + pub fn indicator_border_color(mut self, color: Option) -> Self { + self.indicator_border_color = color; + self + } } impl Disableable for IconButton { @@ -168,6 +182,10 @@ impl RenderOnce for IconButton { .toggle_state(is_selected) .selected_icon(self.selected_icon) .when_some(selected_style, |this, style| this.selected_style(style)) + .when_some(self.indicator, |this, indicator| { + this.indicator(indicator) + .indicator_border_color(self.indicator_border_color) + }) .size(self.icon_size) .color(Color::Custom(color)), )