Detailed changes
@@ -11,7 +11,7 @@ use project::DisableAiSettings;
use settings::{Settings, update_settings_file};
use ui::{
Badge, ButtonLike, Divider, Modal, ModalFooter, ModalHeader, Section, SwitchField, ToggleState,
- prelude::*,
+ prelude::*, tooltip_container,
};
use util::ResultExt;
use workspace::ModalView;
@@ -41,7 +41,11 @@ fn render_llm_provider_section(
}
fn render_privacy_card(disabled: bool, cx: &mut App) -> impl IntoElement {
- let privacy_badge = || Badge::new("Privacy").icon(IconName::ShieldCheck);
+ let privacy_badge = || {
+ Badge::new("Privacy")
+ .icon(IconName::ShieldCheck)
+ .tooltip(move |_, cx| cx.new(|_| AiPrivacyTooltip::new()).into())
+ };
v_flex()
.relative()
@@ -355,3 +359,37 @@ impl Render for AiConfigurationModal {
)
}
}
+
+pub struct AiPrivacyTooltip {}
+
+impl AiPrivacyTooltip {
+ pub fn new() -> Self {
+ Self {}
+ }
+}
+
+impl Render for AiPrivacyTooltip {
+ fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+ const DESCRIPTION: &'static str = "One of Zed's most important principles is transparency. This is why we are and value open-source so much. And it wouldn't be any different with AI.";
+
+ tooltip_container(window, cx, move |this, _, _| {
+ this.child(
+ h_flex()
+ .gap_1()
+ .child(
+ Icon::new(IconName::ShieldCheck)
+ .size(IconSize::Small)
+ .color(Color::Muted),
+ )
+ .child(Label::new("Privacy Principle")),
+ )
+ .child(
+ div().max_w_64().child(
+ Label::new(DESCRIPTION)
+ .size(LabelSize::Small)
+ .color(Color::Muted),
+ ),
+ )
+ })
+ }
+}
@@ -9,7 +9,7 @@ use settings::{Settings as _, update_settings_file};
use theme::{FontFamilyCache, FontFamilyName, ThemeSettings};
use ui::{
ButtonLike, ContextMenu, DropdownMenu, NumericStepper, SwitchField, ToggleButtonGroup,
- ToggleButtonGroupStyle, ToggleButtonSimple, ToggleState, prelude::*,
+ ToggleButtonGroupStyle, ToggleButtonSimple, ToggleState, Tooltip, prelude::*,
};
use crate::{ImportCursorSettings, ImportVsCodeSettings};
@@ -357,34 +357,65 @@ fn render_font_customization_section(window: &mut Window, cx: &mut App) -> impl
}
fn render_popular_settings_section(window: &mut Window, cx: &mut App) -> impl IntoElement {
+ const LIGATURE_TOOLTIP: &'static str = "Ligatures are when a font creates a special character out of combining two characters into one. For example, with ligatures turned on, =/= would become ≠.";
+
v_flex()
.gap_5()
.child(Label::new("Popular Settings").size(LabelSize::Large).mt_8())
.child(render_font_customization_section(window, cx))
+ .child(
+ SwitchField::new(
+ "onboarding-font-ligatures",
+ "Font Ligatures",
+ Some("Combine text characters into their associated symbols.".into()),
+ if read_font_ligatures(cx) {
+ ui::ToggleState::Selected
+ } else {
+ ui::ToggleState::Unselected
+ },
+ |toggle_state, _, cx| {
+ write_font_ligatures(toggle_state == &ToggleState::Selected, cx);
+ },
+ )
+ .tooltip(Tooltip::text(LIGATURE_TOOLTIP)),
+ )
.child(SwitchField::new(
- "onboarding-font-ligatures",
- "Font Ligatures",
- Some("Combine text characters into their associated symbols.".into()),
- if read_font_ligatures(cx) {
+ "onboarding-format-on-save",
+ "Format on Save",
+ Some("Format code automatically when saving.".into()),
+ if read_format_on_save(cx) {
ui::ToggleState::Selected
} else {
ui::ToggleState::Unselected
},
|toggle_state, _, cx| {
- write_font_ligatures(toggle_state == &ToggleState::Selected, cx);
+ write_format_on_save(toggle_state == &ToggleState::Selected, cx);
},
))
.child(SwitchField::new(
- "onboarding-format-on-save",
- "Format on Save",
- Some("Format code automatically when saving.".into()),
- if read_format_on_save(cx) {
+ "onboarding-enable-inlay-hints",
+ "Inlay Hints",
+ Some("See parameter names for function and method calls inline.".into()),
+ if read_inlay_hints(cx) {
ui::ToggleState::Selected
} else {
ui::ToggleState::Unselected
},
|toggle_state, _, cx| {
- write_format_on_save(toggle_state == &ToggleState::Selected, cx);
+ write_inlay_hints(toggle_state == &ToggleState::Selected, cx);
+ },
+ ))
+ .child(SwitchField::new(
+ "onboarding-git-blame-switch",
+ "Git Blame",
+ Some("See who committed each line on a given file.".into()),
+ if read_git_blame(cx) {
+ ui::ToggleState::Selected
+ } else {
+ ui::ToggleState::Unselected
+ },
+ |toggle_state, _, cx| {
+ set_git_blame(toggle_state == &ToggleState::Selected, cx);
},
))
.child(
@@ -421,32 +452,6 @@ fn render_popular_settings_section(window: &mut Window, cx: &mut App) -> impl In
.button_width(ui::rems_from_px(64.)),
),
)
- .child(SwitchField::new(
- "onboarding-enable-inlay-hints",
- "Inlay Hints",
- Some("See parameter names for function and method calls inline.".into()),
- if read_inlay_hints(cx) {
- ui::ToggleState::Selected
- } else {
- ui::ToggleState::Unselected
- },
- |toggle_state, _, cx| {
- write_inlay_hints(toggle_state == &ToggleState::Selected, cx);
- },
- ))
- .child(SwitchField::new(
- "onboarding-git-blame-switch",
- "Git Blame",
- Some("See who committed each line on a given file.".into()),
- if read_git_blame(cx) {
- ui::ToggleState::Selected
- } else {
- ui::ToggleState::Unselected
- },
- |toggle_state, _, cx| {
- set_git_blame(toggle_state == &ToggleState::Selected, cx);
- },
- ))
}
pub(crate) fn render_editing_page(window: &mut Window, cx: &mut App) -> impl IntoElement {
@@ -1,13 +1,18 @@
+use std::rc::Rc;
+
use crate::Divider;
use crate::DividerColor;
+use crate::Tooltip;
use crate::component_prelude::*;
use crate::prelude::*;
+use gpui::AnyView;
use gpui::{AnyElement, IntoElement, SharedString, Window};
#[derive(IntoElement, RegisterComponent)]
pub struct Badge {
label: SharedString,
icon: IconName,
+ tooltip: Option<Rc<dyn Fn(&mut Window, &mut App) -> AnyView>>,
}
impl Badge {
@@ -15,6 +20,7 @@ impl Badge {
Self {
label: label.into(),
icon: IconName::Check,
+ tooltip: None,
}
}
@@ -22,11 +28,19 @@ impl Badge {
self.icon = icon;
self
}
+
+ pub fn tooltip(mut self, tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self {
+ self.tooltip = Some(Rc::new(tooltip));
+ self
+ }
}
impl RenderOnce for Badge {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
+ let tooltip = self.tooltip;
+
h_flex()
+ .id(self.label.clone())
.h_full()
.gap_1()
.pl_1()
@@ -43,6 +57,9 @@ impl RenderOnce for Badge {
)
.child(Divider::vertical().color(DividerColor::Border))
.child(Label::new(self.label.clone()).size(LabelSize::Small).ml_1())
+ .when_some(tooltip, |this, tooltip| {
+ this.tooltip(move |window, cx| tooltip(window, cx))
+ })
}
}
@@ -59,7 +76,18 @@ impl Component for Badge {
fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
Some(
- single_example("Basic Badge", Badge::new("Default").into_any_element())
+ v_flex()
+ .gap_6()
+ .child(single_example(
+ "Basic Badge",
+ Badge::new("Default").into_any_element(),
+ ))
+ .child(single_example(
+ "With Tooltip",
+ Badge::new("Tooltip")
+ .tooltip(Tooltip::text("This is a tooltip."))
+ .into_any_element(),
+ ))
.into_any_element(),
)
}
@@ -2,10 +2,10 @@ use gpui::{
AnyElement, AnyView, ClickEvent, ElementId, Hsla, IntoElement, Styled, Window, div, hsla,
prelude::*,
};
-use std::sync::Arc;
+use std::{rc::Rc, sync::Arc};
use crate::utils::is_light;
-use crate::{Color, Icon, IconName, ToggleState};
+use crate::{Color, Icon, IconName, ToggleState, Tooltip};
use crate::{ElevationIndex, KeyBinding, prelude::*};
// TODO: Checkbox, CheckboxWithLabel, and Switch could all be
@@ -571,6 +571,7 @@ pub struct SwitchField {
on_click: Arc<dyn Fn(&ToggleState, &mut Window, &mut App) + 'static>,
disabled: bool,
color: SwitchColor,
+ tooltip: Option<Rc<dyn Fn(&mut Window, &mut App) -> AnyView>>,
}
impl SwitchField {
@@ -589,6 +590,7 @@ impl SwitchField {
on_click: Arc::new(on_click),
disabled: false,
color: SwitchColor::Accent,
+ tooltip: None,
}
}
@@ -608,10 +610,17 @@ impl SwitchField {
self.color = color;
self
}
+
+ pub fn tooltip(mut self, tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self {
+ self.tooltip = Some(Rc::new(tooltip));
+ self
+ }
}
impl RenderOnce for SwitchField {
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
+ let tooltip = self.tooltip;
+
h_flex()
.id(SharedString::from(format!("{}-container", self.id)))
.when(!self.disabled, |this| {
@@ -621,14 +630,48 @@ impl RenderOnce for SwitchField {
.gap_4()
.justify_between()
.flex_wrap()
- .child(match &self.description {
- Some(description) => v_flex()
+ .child(match (&self.description, &tooltip) {
+ (Some(description), Some(tooltip)) => v_flex()
+ .gap_0p5()
+ .max_w_5_6()
+ .child(
+ h_flex()
+ .gap_0p5()
+ .child(Label::new(self.label.clone()))
+ .child(
+ IconButton::new("tooltip_button", IconName::Info)
+ .icon_size(IconSize::XSmall)
+ .icon_color(Color::Muted)
+ .shape(crate::IconButtonShape::Square)
+ .tooltip({
+ let tooltip = tooltip.clone();
+ move |window, cx| tooltip(window, cx)
+ }),
+ ),
+ )
+ .child(Label::new(description.clone()).color(Color::Muted))
+ .into_any_element(),
+ (Some(description), None) => v_flex()
.gap_0p5()
.max_w_5_6()
.child(Label::new(self.label.clone()))
.child(Label::new(description.clone()).color(Color::Muted))
.into_any_element(),
- None => Label::new(self.label.clone()).into_any_element(),
+ (None, Some(tooltip)) => h_flex()
+ .gap_0p5()
+ .child(Label::new(self.label.clone()))
+ .child(
+ IconButton::new("tooltip_button", IconName::Info)
+ .icon_size(IconSize::XSmall)
+ .icon_color(Color::Muted)
+ .shape(crate::IconButtonShape::Square)
+ .tooltip({
+ let tooltip = tooltip.clone();
+ move |window, cx| tooltip(window, cx)
+ }),
+ )
+ .into_any_element(),
+ (None, None) => Label::new(self.label.clone()).into_any_element(),
})
.child(
Switch::new(
@@ -754,6 +797,35 @@ impl Component for SwitchField {
.into_any_element(),
)],
),
+ example_group_with_title(
+ "With Tooltip",
+ vec![
+ single_example(
+ "Tooltip with Description",
+ SwitchField::new(
+ "switch_field_tooltip_with_desc",
+ "Nice Feature",
+ Some("Enable advanced configuration options.".into()),
+ ToggleState::Unselected,
+ |_, _, _| {},
+ )
+ .tooltip(Tooltip::text("This is content for this tooltip!"))
+ .into_any_element(),
+ ),
+ single_example(
+ "Tooltip without Description",
+ SwitchField::new(
+ "switch_field_tooltip_no_desc",
+ "Nice Feature",
+ None,
+ ToggleState::Selected,
+ |_, _, _| {},
+ )
+ .tooltip(Tooltip::text("This is content for this tooltip!"))
+ .into_any_element(),
+ ),
+ ],
+ ),
])
.into_any_element(),
)