Merge remote-tracking branch 'origin/main' into project_panel/open_split
This commit is contained in:
commit
730816d56f
16 changed files with 574 additions and 290 deletions
1
.github/actionlint.yml
vendored
1
.github/actionlint.yml
vendored
|
@ -24,7 +24,6 @@ self-hosted-runner:
|
|||
- buildjet-8vcpu-ubuntu-2204-arm
|
||||
- buildjet-16vcpu-ubuntu-2204-arm
|
||||
- buildjet-32vcpu-ubuntu-2204-arm
|
||||
- buildjet-64vcpu-ubuntu-2204-arm
|
||||
# Self Hosted Runners
|
||||
- self-mini-macos
|
||||
- self-32vcpu-windows-2022
|
||||
|
|
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
@ -650,7 +650,7 @@ jobs:
|
|||
timeout-minutes: 60
|
||||
name: Linux arm64 release bundle
|
||||
runs-on:
|
||||
- buildjet-16vcpu-ubuntu-2204-arm
|
||||
- buildjet-32vcpu-ubuntu-2204-arm
|
||||
if: |
|
||||
startsWith(github.ref, 'refs/tags/v')
|
||||
|| contains(github.event.pull_request.labels.*.name, 'run-bundling')
|
||||
|
|
2
.github/workflows/release_nightly.yml
vendored
2
.github/workflows/release_nightly.yml
vendored
|
@ -168,7 +168,7 @@ jobs:
|
|||
name: Create a Linux *.tar.gz bundle for ARM
|
||||
if: github.repository_owner == 'zed-industries'
|
||||
runs-on:
|
||||
- buildjet-16vcpu-ubuntu-2204-arm
|
||||
- buildjet-32vcpu-ubuntu-2204-arm
|
||||
needs: tests
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
|
|
|
@ -1175,7 +1175,8 @@
|
|||
"bindings": {
|
||||
"ctrl-1": "onboarding::ActivateBasicsPage",
|
||||
"ctrl-2": "onboarding::ActivateEditingPage",
|
||||
"ctrl-3": "onboarding::ActivateAISetupPage"
|
||||
"ctrl-3": "onboarding::ActivateAISetupPage",
|
||||
"ctrl-escape": "onboarding::Finish"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1277,7 +1277,8 @@
|
|||
"bindings": {
|
||||
"cmd-1": "onboarding::ActivateBasicsPage",
|
||||
"cmd-2": "onboarding::ActivateEditingPage",
|
||||
"cmd-3": "onboarding::ActivateAISetupPage"
|
||||
"cmd-3": "onboarding::ActivateAISetupPage",
|
||||
"cmd-escape": "onboarding::Finish"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -12,6 +12,7 @@ pub struct AiUpsellCard {
|
|||
pub sign_in_status: SignInStatus,
|
||||
pub sign_in: Arc<dyn Fn(&mut Window, &mut App)>,
|
||||
pub user_plan: Option<Plan>,
|
||||
pub tab_index: Option<isize>,
|
||||
}
|
||||
|
||||
impl AiUpsellCard {
|
||||
|
@ -28,6 +29,7 @@ impl AiUpsellCard {
|
|||
})
|
||||
.detach_and_log_err(cx);
|
||||
}),
|
||||
tab_index: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -112,7 +114,8 @@ impl RenderOnce for AiUpsellCard {
|
|||
.on_click(move |_, _window, cx| {
|
||||
telemetry::event!("Start Trial Clicked", state = "post-sign-in");
|
||||
cx.open_url(&zed_urls::start_trial_url(cx))
|
||||
}),
|
||||
})
|
||||
.when_some(self.tab_index, |this, tab_index| this.tab_index(tab_index)),
|
||||
)
|
||||
.child(
|
||||
Label::new("No credit card required")
|
||||
|
@ -123,6 +126,7 @@ impl RenderOnce for AiUpsellCard {
|
|||
_ => Button::new("sign_in", "Sign In")
|
||||
.full_width()
|
||||
.style(ButtonStyle::Tinted(ui::TintColor::Accent))
|
||||
.when_some(self.tab_index, |this, tab_index| this.tab_index(tab_index))
|
||||
.on_click({
|
||||
let callback = self.sign_in.clone();
|
||||
move |_, window, cx| {
|
||||
|
@ -193,6 +197,7 @@ impl Component for AiUpsellCard {
|
|||
sign_in_status: SignInStatus::SignedOut,
|
||||
sign_in: Arc::new(|_, _| {}),
|
||||
user_plan: None,
|
||||
tab_index: Some(0),
|
||||
}
|
||||
.into_any_element(),
|
||||
),
|
||||
|
@ -202,6 +207,7 @@ impl Component for AiUpsellCard {
|
|||
sign_in_status: SignInStatus::SignedIn,
|
||||
sign_in: Arc::new(|_, _| {}),
|
||||
user_plan: None,
|
||||
tab_index: Some(1),
|
||||
}
|
||||
.into_any_element(),
|
||||
),
|
||||
|
|
|
@ -36,11 +36,18 @@ pub enum AnthropicModelMode {
|
|||
pub enum Model {
|
||||
#[serde(rename = "claude-opus-4", alias = "claude-opus-4-latest")]
|
||||
ClaudeOpus4,
|
||||
#[serde(rename = "claude-opus-4-1", alias = "claude-opus-4-1-latest")]
|
||||
ClaudeOpus4_1,
|
||||
#[serde(
|
||||
rename = "claude-opus-4-thinking",
|
||||
alias = "claude-opus-4-thinking-latest"
|
||||
)]
|
||||
ClaudeOpus4Thinking,
|
||||
#[serde(
|
||||
rename = "claude-opus-4-1-thinking",
|
||||
alias = "claude-opus-4-1-thinking-latest"
|
||||
)]
|
||||
ClaudeOpus4_1Thinking,
|
||||
#[default]
|
||||
#[serde(rename = "claude-sonnet-4", alias = "claude-sonnet-4-latest")]
|
||||
ClaudeSonnet4,
|
||||
|
@ -91,10 +98,18 @@ impl Model {
|
|||
}
|
||||
|
||||
pub fn from_id(id: &str) -> Result<Self> {
|
||||
if id.starts_with("claude-opus-4-1-thinking") {
|
||||
return Ok(Self::ClaudeOpus4_1Thinking);
|
||||
}
|
||||
|
||||
if id.starts_with("claude-opus-4-thinking") {
|
||||
return Ok(Self::ClaudeOpus4Thinking);
|
||||
}
|
||||
|
||||
if id.starts_with("claude-opus-4-1") {
|
||||
return Ok(Self::ClaudeOpus4_1);
|
||||
}
|
||||
|
||||
if id.starts_with("claude-opus-4") {
|
||||
return Ok(Self::ClaudeOpus4);
|
||||
}
|
||||
|
@ -141,7 +156,9 @@ impl Model {
|
|||
pub fn id(&self) -> &str {
|
||||
match self {
|
||||
Self::ClaudeOpus4 => "claude-opus-4-latest",
|
||||
Self::ClaudeOpus4_1 => "claude-opus-4-1-latest",
|
||||
Self::ClaudeOpus4Thinking => "claude-opus-4-thinking-latest",
|
||||
Self::ClaudeOpus4_1Thinking => "claude-opus-4-1-thinking-latest",
|
||||
Self::ClaudeSonnet4 => "claude-sonnet-4-latest",
|
||||
Self::ClaudeSonnet4Thinking => "claude-sonnet-4-thinking-latest",
|
||||
Self::Claude3_5Sonnet => "claude-3-5-sonnet-latest",
|
||||
|
@ -159,6 +176,7 @@ impl Model {
|
|||
pub fn request_id(&self) -> &str {
|
||||
match self {
|
||||
Self::ClaudeOpus4 | Self::ClaudeOpus4Thinking => "claude-opus-4-20250514",
|
||||
Self::ClaudeOpus4_1 | Self::ClaudeOpus4_1Thinking => "claude-opus-4-1-20250805",
|
||||
Self::ClaudeSonnet4 | Self::ClaudeSonnet4Thinking => "claude-sonnet-4-20250514",
|
||||
Self::Claude3_5Sonnet => "claude-3-5-sonnet-latest",
|
||||
Self::Claude3_7Sonnet | Self::Claude3_7SonnetThinking => "claude-3-7-sonnet-latest",
|
||||
|
@ -173,7 +191,9 @@ impl Model {
|
|||
pub fn display_name(&self) -> &str {
|
||||
match self {
|
||||
Self::ClaudeOpus4 => "Claude Opus 4",
|
||||
Self::ClaudeOpus4_1 => "Claude Opus 4.1",
|
||||
Self::ClaudeOpus4Thinking => "Claude Opus 4 Thinking",
|
||||
Self::ClaudeOpus4_1Thinking => "Claude Opus 4.1 Thinking",
|
||||
Self::ClaudeSonnet4 => "Claude Sonnet 4",
|
||||
Self::ClaudeSonnet4Thinking => "Claude Sonnet 4 Thinking",
|
||||
Self::Claude3_7Sonnet => "Claude 3.7 Sonnet",
|
||||
|
@ -192,7 +212,9 @@ impl Model {
|
|||
pub fn cache_configuration(&self) -> Option<AnthropicModelCacheConfiguration> {
|
||||
match self {
|
||||
Self::ClaudeOpus4
|
||||
| Self::ClaudeOpus4_1
|
||||
| Self::ClaudeOpus4Thinking
|
||||
| Self::ClaudeOpus4_1Thinking
|
||||
| Self::ClaudeSonnet4
|
||||
| Self::ClaudeSonnet4Thinking
|
||||
| Self::Claude3_5Sonnet
|
||||
|
@ -215,7 +237,9 @@ impl Model {
|
|||
pub fn max_token_count(&self) -> u64 {
|
||||
match self {
|
||||
Self::ClaudeOpus4
|
||||
| Self::ClaudeOpus4_1
|
||||
| Self::ClaudeOpus4Thinking
|
||||
| Self::ClaudeOpus4_1Thinking
|
||||
| Self::ClaudeSonnet4
|
||||
| Self::ClaudeSonnet4Thinking
|
||||
| Self::Claude3_5Sonnet
|
||||
|
@ -232,7 +256,9 @@ impl Model {
|
|||
pub fn max_output_tokens(&self) -> u64 {
|
||||
match self {
|
||||
Self::ClaudeOpus4
|
||||
| Self::ClaudeOpus4_1
|
||||
| Self::ClaudeOpus4Thinking
|
||||
| Self::ClaudeOpus4_1Thinking
|
||||
| Self::ClaudeSonnet4
|
||||
| Self::ClaudeSonnet4Thinking
|
||||
| Self::Claude3_5Sonnet
|
||||
|
@ -249,7 +275,9 @@ impl Model {
|
|||
pub fn default_temperature(&self) -> f32 {
|
||||
match self {
|
||||
Self::ClaudeOpus4
|
||||
| Self::ClaudeOpus4_1
|
||||
| Self::ClaudeOpus4Thinking
|
||||
| Self::ClaudeOpus4_1Thinking
|
||||
| Self::ClaudeSonnet4
|
||||
| Self::ClaudeSonnet4Thinking
|
||||
| Self::Claude3_5Sonnet
|
||||
|
@ -269,6 +297,7 @@ impl Model {
|
|||
pub fn mode(&self) -> AnthropicModelMode {
|
||||
match self {
|
||||
Self::ClaudeOpus4
|
||||
| Self::ClaudeOpus4_1
|
||||
| Self::ClaudeSonnet4
|
||||
| Self::Claude3_5Sonnet
|
||||
| Self::Claude3_7Sonnet
|
||||
|
@ -277,6 +306,7 @@ impl Model {
|
|||
| Self::Claude3Sonnet
|
||||
| Self::Claude3Haiku => AnthropicModelMode::Default,
|
||||
Self::ClaudeOpus4Thinking
|
||||
| Self::ClaudeOpus4_1Thinking
|
||||
| Self::ClaudeSonnet4Thinking
|
||||
| Self::Claude3_7SonnetThinking => AnthropicModelMode::Thinking {
|
||||
budget_tokens: Some(4_096),
|
||||
|
|
|
@ -32,11 +32,18 @@ pub enum Model {
|
|||
ClaudeSonnet4Thinking,
|
||||
#[serde(rename = "claude-opus-4", alias = "claude-opus-4-latest")]
|
||||
ClaudeOpus4,
|
||||
#[serde(rename = "claude-opus-4-1", alias = "claude-opus-4-1-latest")]
|
||||
ClaudeOpus4_1,
|
||||
#[serde(
|
||||
rename = "claude-opus-4-thinking",
|
||||
alias = "claude-opus-4-thinking-latest"
|
||||
)]
|
||||
ClaudeOpus4Thinking,
|
||||
#[serde(
|
||||
rename = "claude-opus-4-1-thinking",
|
||||
alias = "claude-opus-4-1-thinking-latest"
|
||||
)]
|
||||
ClaudeOpus4_1Thinking,
|
||||
#[serde(rename = "claude-3-5-sonnet-v2", alias = "claude-3-5-sonnet-latest")]
|
||||
Claude3_5SonnetV2,
|
||||
#[serde(rename = "claude-3-7-sonnet", alias = "claude-3-7-sonnet-latest")]
|
||||
|
@ -147,7 +154,9 @@ impl Model {
|
|||
Model::ClaudeSonnet4 => "claude-4-sonnet",
|
||||
Model::ClaudeSonnet4Thinking => "claude-4-sonnet-thinking",
|
||||
Model::ClaudeOpus4 => "claude-4-opus",
|
||||
Model::ClaudeOpus4_1 => "claude-4-opus-1",
|
||||
Model::ClaudeOpus4Thinking => "claude-4-opus-thinking",
|
||||
Model::ClaudeOpus4_1Thinking => "claude-4-opus-1-thinking",
|
||||
Model::Claude3_5SonnetV2 => "claude-3-5-sonnet-v2",
|
||||
Model::Claude3_5Sonnet => "claude-3-5-sonnet",
|
||||
Model::Claude3Opus => "claude-3-opus",
|
||||
|
@ -208,6 +217,9 @@ impl Model {
|
|||
Model::ClaudeOpus4 | Model::ClaudeOpus4Thinking => {
|
||||
"anthropic.claude-opus-4-20250514-v1:0"
|
||||
}
|
||||
Model::ClaudeOpus4_1 | Model::ClaudeOpus4_1Thinking => {
|
||||
"anthropic.claude-opus-4-1-20250805-v1:0"
|
||||
}
|
||||
Model::Claude3_5SonnetV2 => "anthropic.claude-3-5-sonnet-20241022-v2:0",
|
||||
Model::Claude3_5Sonnet => "anthropic.claude-3-5-sonnet-20240620-v1:0",
|
||||
Model::Claude3Opus => "anthropic.claude-3-opus-20240229-v1:0",
|
||||
|
@ -266,7 +278,9 @@ impl Model {
|
|||
Self::ClaudeSonnet4 => "Claude Sonnet 4",
|
||||
Self::ClaudeSonnet4Thinking => "Claude Sonnet 4 Thinking",
|
||||
Self::ClaudeOpus4 => "Claude Opus 4",
|
||||
Self::ClaudeOpus4_1 => "Claude Opus 4.1",
|
||||
Self::ClaudeOpus4Thinking => "Claude Opus 4 Thinking",
|
||||
Self::ClaudeOpus4_1Thinking => "Claude Opus 4.1 Thinking",
|
||||
Self::Claude3_5SonnetV2 => "Claude 3.5 Sonnet v2",
|
||||
Self::Claude3_5Sonnet => "Claude 3.5 Sonnet",
|
||||
Self::Claude3Opus => "Claude 3 Opus",
|
||||
|
@ -330,8 +344,10 @@ impl Model {
|
|||
| Self::Claude3_7Sonnet
|
||||
| Self::ClaudeSonnet4
|
||||
| Self::ClaudeOpus4
|
||||
| Self::ClaudeOpus4_1
|
||||
| Self::ClaudeSonnet4Thinking
|
||||
| Self::ClaudeOpus4Thinking => 200_000,
|
||||
| Self::ClaudeOpus4Thinking
|
||||
| Self::ClaudeOpus4_1Thinking => 200_000,
|
||||
Self::AmazonNovaPremier => 1_000_000,
|
||||
Self::PalmyraWriterX5 => 1_000_000,
|
||||
Self::PalmyraWriterX4 => 128_000,
|
||||
|
@ -348,7 +364,9 @@ impl Model {
|
|||
| Self::ClaudeSonnet4
|
||||
| Self::ClaudeSonnet4Thinking
|
||||
| Self::ClaudeOpus4
|
||||
| Model::ClaudeOpus4Thinking => 128_000,
|
||||
| Model::ClaudeOpus4Thinking
|
||||
| Self::ClaudeOpus4_1
|
||||
| Model::ClaudeOpus4_1Thinking => 128_000,
|
||||
Self::Claude3_5SonnetV2 | Self::PalmyraWriterX4 | Self::PalmyraWriterX5 => 8_192,
|
||||
Self::Custom {
|
||||
max_output_tokens, ..
|
||||
|
@ -366,6 +384,8 @@ impl Model {
|
|||
| Self::Claude3_7Sonnet
|
||||
| Self::ClaudeOpus4
|
||||
| Self::ClaudeOpus4Thinking
|
||||
| Self::ClaudeOpus4_1
|
||||
| Self::ClaudeOpus4_1Thinking
|
||||
| Self::ClaudeSonnet4
|
||||
| Self::ClaudeSonnet4Thinking => 1.0,
|
||||
Self::Custom {
|
||||
|
@ -387,6 +407,8 @@ impl Model {
|
|||
| Self::Claude3_7SonnetThinking
|
||||
| Self::ClaudeOpus4
|
||||
| Self::ClaudeOpus4Thinking
|
||||
| Self::ClaudeOpus4_1
|
||||
| Self::ClaudeOpus4_1Thinking
|
||||
| Self::ClaudeSonnet4
|
||||
| Self::ClaudeSonnet4Thinking
|
||||
| Self::Claude3_5Haiku => true,
|
||||
|
@ -420,7 +442,9 @@ impl Model {
|
|||
| Self::ClaudeSonnet4
|
||||
| Self::ClaudeSonnet4Thinking
|
||||
| Self::ClaudeOpus4
|
||||
| Self::ClaudeOpus4Thinking => true,
|
||||
| Self::ClaudeOpus4Thinking
|
||||
| Self::ClaudeOpus4_1
|
||||
| Self::ClaudeOpus4_1Thinking => true,
|
||||
|
||||
// Custom models - check if they have cache configuration
|
||||
Self::Custom {
|
||||
|
@ -440,7 +464,9 @@ impl Model {
|
|||
| Self::ClaudeSonnet4
|
||||
| Self::ClaudeSonnet4Thinking
|
||||
| Self::ClaudeOpus4
|
||||
| Self::ClaudeOpus4Thinking => Some(BedrockModelCacheConfiguration {
|
||||
| Self::ClaudeOpus4Thinking
|
||||
| Self::ClaudeOpus4_1
|
||||
| Self::ClaudeOpus4_1Thinking => Some(BedrockModelCacheConfiguration {
|
||||
max_cache_anchors: 4,
|
||||
min_total_token: 1024,
|
||||
}),
|
||||
|
@ -467,9 +493,11 @@ impl Model {
|
|||
Model::ClaudeSonnet4Thinking => BedrockModelMode::Thinking {
|
||||
budget_tokens: Some(4096),
|
||||
},
|
||||
Model::ClaudeOpus4Thinking => BedrockModelMode::Thinking {
|
||||
budget_tokens: Some(4096),
|
||||
},
|
||||
Model::ClaudeOpus4Thinking | Model::ClaudeOpus4_1Thinking => {
|
||||
BedrockModelMode::Thinking {
|
||||
budget_tokens: Some(4096),
|
||||
}
|
||||
}
|
||||
_ => BedrockModelMode::Default,
|
||||
}
|
||||
}
|
||||
|
@ -518,6 +546,8 @@ impl Model {
|
|||
| Model::ClaudeSonnet4Thinking
|
||||
| Model::ClaudeOpus4
|
||||
| Model::ClaudeOpus4Thinking
|
||||
| Model::ClaudeOpus4_1
|
||||
| Model::ClaudeOpus4_1Thinking
|
||||
| Model::Claude3Haiku
|
||||
| Model::Claude3Opus
|
||||
| Model::Claude3Sonnet
|
||||
|
|
|
@ -4699,6 +4699,8 @@ pub enum ElementId {
|
|||
Path(Arc<std::path::Path>),
|
||||
/// A code location.
|
||||
CodeLocation(core::panic::Location<'static>),
|
||||
/// A labeled child of an element.
|
||||
NamedChild(Box<ElementId>, SharedString),
|
||||
}
|
||||
|
||||
impl ElementId {
|
||||
|
@ -4719,6 +4721,7 @@ impl Display for ElementId {
|
|||
ElementId::Uuid(uuid) => write!(f, "{}", uuid)?,
|
||||
ElementId::Path(path) => write!(f, "{}", path.display())?,
|
||||
ElementId::CodeLocation(location) => write!(f, "{}", location)?,
|
||||
ElementId::NamedChild(id, name) => write!(f, "{}-{}", id, name)?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -4809,6 +4812,12 @@ impl From<(&'static str, u32)> for ElementId {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Into<SharedString>> From<(ElementId, T)> for ElementId {
|
||||
fn from((id, name): (ElementId, T)) -> Self {
|
||||
ElementId::NamedChild(Box::new(id), name.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// A rectangle to be rendered in the window at the given position and size.
|
||||
/// Passed as an argument [`Window::paint_quad`].
|
||||
#[derive(Clone)]
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use ai_onboarding::{AiUpsellCard, SignInStatus};
|
||||
use client::UserStore;
|
||||
use fs::Fs;
|
||||
use gpui::{
|
||||
Action, AnyView, App, DismissEvent, EventEmitter, FocusHandle, Focusable, Window, prelude::*,
|
||||
Action, AnyView, App, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, WeakEntity,
|
||||
Window, prelude::*,
|
||||
};
|
||||
use itertools;
|
||||
use language_model::{LanguageModelProvider, LanguageModelProviderId, LanguageModelRegistry};
|
||||
|
@ -14,15 +16,14 @@ use ui::{
|
|||
prelude::*, tooltip_container,
|
||||
};
|
||||
use util::ResultExt;
|
||||
use workspace::ModalView;
|
||||
use workspace::{ModalView, Workspace};
|
||||
use zed_actions::agent::OpenSettings;
|
||||
|
||||
use crate::Onboarding;
|
||||
|
||||
const FEATURED_PROVIDERS: [&'static str; 4] = ["anthropic", "google", "openai", "ollama"];
|
||||
|
||||
fn render_llm_provider_section(
|
||||
onboarding: &Onboarding,
|
||||
tab_index: &mut isize,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
disabled: bool,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
|
@ -37,10 +38,10 @@ fn render_llm_provider_section(
|
|||
.color(Color::Muted),
|
||||
),
|
||||
)
|
||||
.child(render_llm_provider_card(onboarding, disabled, window, cx))
|
||||
.child(render_llm_provider_card(tab_index, workspace, disabled, window, cx))
|
||||
}
|
||||
|
||||
fn render_privacy_card(disabled: bool, cx: &mut App) -> impl IntoElement {
|
||||
fn render_privacy_card(tab_index: &mut isize, disabled: bool, cx: &mut App) -> impl IntoElement {
|
||||
let privacy_badge = || {
|
||||
Badge::new("Privacy")
|
||||
.icon(IconName::ShieldCheck)
|
||||
|
@ -98,6 +99,10 @@ fn render_privacy_card(disabled: bool, cx: &mut App) -> impl IntoElement {
|
|||
.icon_color(Color::Muted)
|
||||
.on_click(|_, _, cx| {
|
||||
cx.open_url("https://zed.dev/docs/ai/privacy-and-security");
|
||||
})
|
||||
.tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index - 1
|
||||
}),
|
||||
),
|
||||
),
|
||||
|
@ -114,7 +119,8 @@ fn render_privacy_card(disabled: bool, cx: &mut App) -> impl IntoElement {
|
|||
}
|
||||
|
||||
fn render_llm_provider_card(
|
||||
onboarding: &Onboarding,
|
||||
tab_index: &mut isize,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
disabled: bool,
|
||||
_: &mut Window,
|
||||
cx: &mut App,
|
||||
|
@ -140,6 +146,10 @@ fn render_llm_provider_card(
|
|||
|
||||
ButtonLike::new(("onboarding-ai-setup-buttons", index))
|
||||
.size(ButtonSize::Large)
|
||||
.tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index - 1
|
||||
})
|
||||
.child(
|
||||
h_flex()
|
||||
.group(&group_name)
|
||||
|
@ -188,7 +198,7 @@ fn render_llm_provider_card(
|
|||
),
|
||||
)
|
||||
.on_click({
|
||||
let workspace = onboarding.workspace.clone();
|
||||
let workspace = workspace.clone();
|
||||
move |_, window, cx| {
|
||||
workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
|
@ -219,57 +229,56 @@ fn render_llm_provider_card(
|
|||
.icon_size(IconSize::XSmall)
|
||||
.on_click(|_event, window, cx| {
|
||||
window.dispatch_action(OpenSettings.boxed_clone(), cx)
|
||||
})
|
||||
.tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index - 1
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn render_ai_setup_page(
|
||||
onboarding: &Onboarding,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
user_store: Entity<UserStore>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> impl IntoElement {
|
||||
let mut tab_index = 0;
|
||||
let is_ai_disabled = DisableAiSettings::get_global(cx).disable_ai;
|
||||
|
||||
let backdrop = div()
|
||||
.id("backdrop")
|
||||
.size_full()
|
||||
.absolute()
|
||||
.inset_0()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.opacity(0.8)
|
||||
.block_mouse_except_scroll();
|
||||
|
||||
v_flex()
|
||||
.gap_2()
|
||||
.child(SwitchField::new(
|
||||
"enable_ai",
|
||||
"Enable AI features",
|
||||
None,
|
||||
if is_ai_disabled {
|
||||
ToggleState::Unselected
|
||||
} else {
|
||||
ToggleState::Selected
|
||||
},
|
||||
|toggle_state, _, cx| {
|
||||
let enabled = match toggle_state {
|
||||
ToggleState::Indeterminate => {
|
||||
return;
|
||||
}
|
||||
ToggleState::Unselected => false,
|
||||
ToggleState::Selected => true,
|
||||
};
|
||||
|
||||
let fs = <dyn Fs>::global(cx);
|
||||
update_settings_file::<DisableAiSettings>(
|
||||
fs,
|
||||
cx,
|
||||
move |ai_settings: &mut Option<bool>, _| {
|
||||
*ai_settings = Some(!enabled);
|
||||
},
|
||||
);
|
||||
},
|
||||
))
|
||||
.child(render_privacy_card(is_ai_disabled, cx))
|
||||
.child(
|
||||
SwitchField::new(
|
||||
"enable_ai",
|
||||
"Enable AI features",
|
||||
None,
|
||||
if is_ai_disabled {
|
||||
ToggleState::Unselected
|
||||
} else {
|
||||
ToggleState::Selected
|
||||
},
|
||||
|&toggle_state, _, cx| {
|
||||
let fs = <dyn Fs>::global(cx);
|
||||
update_settings_file::<DisableAiSettings>(
|
||||
fs,
|
||||
cx,
|
||||
move |ai_settings: &mut Option<bool>, _| {
|
||||
*ai_settings = match toggle_state {
|
||||
ToggleState::Indeterminate => None,
|
||||
ToggleState::Unselected => Some(true),
|
||||
ToggleState::Selected => Some(false),
|
||||
};
|
||||
},
|
||||
);
|
||||
},
|
||||
)
|
||||
.tab_index({
|
||||
tab_index += 1;
|
||||
tab_index - 1
|
||||
}),
|
||||
)
|
||||
.child(render_privacy_card(&mut tab_index, is_ai_disabled, cx))
|
||||
.child(
|
||||
v_flex()
|
||||
.mt_2()
|
||||
|
@ -277,15 +286,31 @@ pub(crate) fn render_ai_setup_page(
|
|||
.child(AiUpsellCard {
|
||||
sign_in_status: SignInStatus::SignedIn,
|
||||
sign_in: Arc::new(|_, _| {}),
|
||||
user_plan: onboarding.user_store.read(cx).plan(),
|
||||
user_plan: user_store.read(cx).plan(),
|
||||
tab_index: Some({
|
||||
tab_index += 1;
|
||||
tab_index - 1
|
||||
}),
|
||||
})
|
||||
.child(render_llm_provider_section(
|
||||
onboarding,
|
||||
&mut tab_index,
|
||||
workspace,
|
||||
is_ai_disabled,
|
||||
window,
|
||||
cx,
|
||||
))
|
||||
.when(is_ai_disabled, |this| this.child(backdrop)),
|
||||
.when(is_ai_disabled, |this| {
|
||||
this.child(
|
||||
div()
|
||||
.id("backdrop")
|
||||
.size_full()
|
||||
.absolute()
|
||||
.inset_0()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.opacity(0.8)
|
||||
.block_mouse_except_scroll(),
|
||||
)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::sync::Arc;
|
|||
|
||||
use client::TelemetrySettings;
|
||||
use fs::Fs;
|
||||
use gpui::{App, IntoElement, Window};
|
||||
use gpui::{App, IntoElement};
|
||||
use settings::{BaseKeymap, Settings, update_settings_file};
|
||||
use theme::{
|
||||
Appearance, SystemAppearance, ThemeMode, ThemeName, ThemeRegistry, ThemeSelection,
|
||||
|
@ -16,7 +16,7 @@ use vim_mode_setting::VimModeSetting;
|
|||
|
||||
use crate::theme_preview::{ThemePreviewStyle, ThemePreviewTile};
|
||||
|
||||
fn render_theme_section(_window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
fn render_theme_section(tab_index: &mut isize, cx: &mut App) -> impl IntoElement {
|
||||
let theme_selection = ThemeSettings::get_global(cx).theme_selection.clone();
|
||||
let system_appearance = theme::SystemAppearance::global(cx);
|
||||
let theme_selection = theme_selection.unwrap_or_else(|| ThemeSelection::Dynamic {
|
||||
|
@ -55,6 +55,7 @@ fn render_theme_section(_window: &mut Window, cx: &mut App) -> impl IntoElement
|
|||
)
|
||||
}),
|
||||
)
|
||||
.tab_index(tab_index)
|
||||
.selected_index(theme_mode as usize)
|
||||
.style(ui::ToggleButtonGroupStyle::Outlined)
|
||||
.button_width(rems_from_px(64.)),
|
||||
|
@ -64,10 +65,11 @@ fn render_theme_section(_window: &mut Window, cx: &mut App) -> impl IntoElement
|
|||
h_flex()
|
||||
.gap_4()
|
||||
.justify_between()
|
||||
.children(render_theme_previews(&theme_selection, cx)),
|
||||
.children(render_theme_previews(tab_index, &theme_selection, cx)),
|
||||
);
|
||||
|
||||
fn render_theme_previews(
|
||||
tab_index: &mut isize,
|
||||
theme_selection: &ThemeSelection,
|
||||
cx: &mut App,
|
||||
) -> [impl IntoElement; 3] {
|
||||
|
@ -110,12 +112,12 @@ fn render_theme_section(_window: &mut Window, cx: &mut App) -> impl IntoElement
|
|||
let colors = cx.theme().colors();
|
||||
|
||||
v_flex()
|
||||
.id(name.clone())
|
||||
.w_full()
|
||||
.items_center()
|
||||
.gap_1()
|
||||
.child(
|
||||
h_flex()
|
||||
.id(name.clone())
|
||||
.relative()
|
||||
.w_full()
|
||||
.border_2()
|
||||
|
@ -128,6 +130,20 @@ fn render_theme_section(_window: &mut Window, cx: &mut App) -> impl IntoElement
|
|||
this.opacity(0.8).hover(|s| s.border_color(colors.border))
|
||||
}
|
||||
})
|
||||
.tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index - 1
|
||||
})
|
||||
.focus(|mut style| {
|
||||
style.border_color = Some(colors.border_focused);
|
||||
style
|
||||
})
|
||||
.on_click({
|
||||
let theme_name = theme.name.clone();
|
||||
move |_, _, cx| {
|
||||
write_theme_change(theme_name.clone(), theme_mode, cx);
|
||||
}
|
||||
})
|
||||
.map(|this| {
|
||||
if theme_mode == ThemeMode::System {
|
||||
let (light, dark) = (
|
||||
|
@ -151,12 +167,6 @@ fn render_theme_section(_window: &mut Window, cx: &mut App) -> impl IntoElement
|
|||
.color(Color::Muted)
|
||||
.size(LabelSize::Small),
|
||||
)
|
||||
.on_click({
|
||||
let theme_name = theme.name.clone();
|
||||
move |_, _, cx| {
|
||||
write_theme_change(theme_name.clone(), theme_mode, cx);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
theme_previews
|
||||
|
@ -187,15 +197,7 @@ fn render_theme_section(_window: &mut Window, cx: &mut App) -> impl IntoElement
|
|||
}
|
||||
}
|
||||
|
||||
fn write_keymap_base(keymap_base: BaseKeymap, cx: &App) {
|
||||
let fs = <dyn Fs>::global(cx);
|
||||
|
||||
update_settings_file::<BaseKeymap>(fs, cx, move |setting, _| {
|
||||
*setting = Some(keymap_base);
|
||||
});
|
||||
}
|
||||
|
||||
fn render_telemetry_section(cx: &App) -> impl IntoElement {
|
||||
fn render_telemetry_section(tab_index: &mut isize, cx: &App) -> impl IntoElement {
|
||||
let fs = <dyn Fs>::global(cx);
|
||||
|
||||
v_flex()
|
||||
|
@ -225,7 +227,10 @@ fn render_telemetry_section(cx: &App) -> impl IntoElement {
|
|||
move |setting, _| setting.metrics = Some(enabled),
|
||||
);
|
||||
}},
|
||||
))
|
||||
).tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index
|
||||
}))
|
||||
.child(SwitchField::new(
|
||||
"onboarding-telemetry-crash-reports",
|
||||
"Help Fix Zed",
|
||||
|
@ -251,10 +256,13 @@ fn render_telemetry_section(cx: &App) -> impl IntoElement {
|
|||
);
|
||||
}
|
||||
}
|
||||
))
|
||||
).tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn render_basics_page(window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
fn render_base_keymap_section(tab_index: &mut isize, cx: &mut App) -> impl IntoElement {
|
||||
let base_keymap = match BaseKeymap::get_global(cx) {
|
||||
BaseKeymap::VSCode => Some(0),
|
||||
BaseKeymap::JetBrains => Some(1),
|
||||
|
@ -265,67 +273,89 @@ pub(crate) fn render_basics_page(window: &mut Window, cx: &mut App) -> impl Into
|
|||
BaseKeymap::TextMate | BaseKeymap::None => None,
|
||||
};
|
||||
|
||||
return v_flex().gap_2().child(Label::new("Base Keymap")).child(
|
||||
ToggleButtonGroup::two_rows(
|
||||
"base_keymap_selection",
|
||||
[
|
||||
ToggleButtonWithIcon::new("VS Code", IconName::EditorVsCode, |_, _, cx| {
|
||||
write_keymap_base(BaseKeymap::VSCode, cx);
|
||||
}),
|
||||
ToggleButtonWithIcon::new("Jetbrains", IconName::EditorJetBrains, |_, _, cx| {
|
||||
write_keymap_base(BaseKeymap::JetBrains, cx);
|
||||
}),
|
||||
ToggleButtonWithIcon::new("Sublime Text", IconName::EditorSublime, |_, _, cx| {
|
||||
write_keymap_base(BaseKeymap::SublimeText, cx);
|
||||
}),
|
||||
],
|
||||
[
|
||||
ToggleButtonWithIcon::new("Atom", IconName::EditorAtom, |_, _, cx| {
|
||||
write_keymap_base(BaseKeymap::Atom, cx);
|
||||
}),
|
||||
ToggleButtonWithIcon::new("Emacs", IconName::EditorEmacs, |_, _, cx| {
|
||||
write_keymap_base(BaseKeymap::Emacs, cx);
|
||||
}),
|
||||
ToggleButtonWithIcon::new("Cursor (Beta)", IconName::EditorCursor, |_, _, cx| {
|
||||
write_keymap_base(BaseKeymap::Cursor, cx);
|
||||
}),
|
||||
],
|
||||
)
|
||||
.when_some(base_keymap, |this, base_keymap| {
|
||||
this.selected_index(base_keymap)
|
||||
})
|
||||
.tab_index(tab_index)
|
||||
.button_width(rems_from_px(216.))
|
||||
.size(ui::ToggleButtonGroupSize::Medium)
|
||||
.style(ui::ToggleButtonGroupStyle::Outlined),
|
||||
);
|
||||
|
||||
fn write_keymap_base(keymap_base: BaseKeymap, cx: &App) {
|
||||
let fs = <dyn Fs>::global(cx);
|
||||
|
||||
update_settings_file::<BaseKeymap>(fs, cx, move |setting, _| {
|
||||
*setting = Some(keymap_base);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn render_vim_mode_switch(tab_index: &mut isize, cx: &mut App) -> impl IntoElement {
|
||||
let toggle_state = if VimModeSetting::get_global(cx).0 {
|
||||
ui::ToggleState::Selected
|
||||
} else {
|
||||
ui::ToggleState::Unselected
|
||||
};
|
||||
SwitchField::new(
|
||||
"onboarding-vim-mode",
|
||||
"Vim Mode",
|
||||
Some(
|
||||
"Coming from Neovim? Zed's first-class implementation of Vim Mode has got your back."
|
||||
.into(),
|
||||
),
|
||||
toggle_state,
|
||||
{
|
||||
let fs = <dyn Fs>::global(cx);
|
||||
move |&selection, _, cx| {
|
||||
update_settings_file::<VimModeSetting>(fs.clone(), cx, move |setting, _| {
|
||||
*setting = match selection {
|
||||
ToggleState::Selected => Some(true),
|
||||
ToggleState::Unselected => Some(false),
|
||||
ToggleState::Indeterminate => None,
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
)
|
||||
.tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index - 1
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn render_basics_page(cx: &mut App) -> impl IntoElement {
|
||||
let mut tab_index = 0;
|
||||
v_flex()
|
||||
.gap_6()
|
||||
.child(render_theme_section(window, cx))
|
||||
.child(
|
||||
v_flex().gap_2().child(Label::new("Base Keymap")).child(
|
||||
ToggleButtonGroup::two_rows(
|
||||
"multiple_row_test",
|
||||
[
|
||||
ToggleButtonWithIcon::new("VS Code", IconName::EditorVsCode, |_, _, cx| {
|
||||
write_keymap_base(BaseKeymap::VSCode, cx);
|
||||
}),
|
||||
ToggleButtonWithIcon::new("Jetbrains", IconName::EditorJetBrains, |_, _, cx| {
|
||||
write_keymap_base(BaseKeymap::JetBrains, cx);
|
||||
}),
|
||||
ToggleButtonWithIcon::new("Sublime Text", IconName::EditorSublime, |_, _, cx| {
|
||||
write_keymap_base(BaseKeymap::SublimeText, cx);
|
||||
}),
|
||||
],
|
||||
[
|
||||
ToggleButtonWithIcon::new("Atom", IconName::EditorAtom, |_, _, cx| {
|
||||
write_keymap_base(BaseKeymap::Atom, cx);
|
||||
}),
|
||||
ToggleButtonWithIcon::new("Emacs", IconName::EditorEmacs, |_, _, cx| {
|
||||
write_keymap_base(BaseKeymap::Emacs, cx);
|
||||
}),
|
||||
ToggleButtonWithIcon::new("Cursor (Beta)", IconName::EditorCursor, |_, _, cx| {
|
||||
write_keymap_base(BaseKeymap::Cursor, cx);
|
||||
}),
|
||||
],
|
||||
)
|
||||
.when_some(base_keymap, |this, base_keymap| this.selected_index(base_keymap))
|
||||
.button_width(rems_from_px(216.))
|
||||
.size(ui::ToggleButtonGroupSize::Medium)
|
||||
.style(ui::ToggleButtonGroupStyle::Outlined)
|
||||
),
|
||||
)
|
||||
.child(SwitchField::new(
|
||||
"onboarding-vim-mode",
|
||||
"Vim Mode",
|
||||
Some("Coming from Neovim? Zed's first-class implementation of Vim Mode has got your back.".into()),
|
||||
if VimModeSetting::get_global(cx).0 {
|
||||
ui::ToggleState::Selected
|
||||
} else {
|
||||
ui::ToggleState::Unselected
|
||||
},
|
||||
{
|
||||
let fs = <dyn Fs>::global(cx);
|
||||
move |selection, _, cx| {
|
||||
let enabled = match selection {
|
||||
ToggleState::Selected => true,
|
||||
ToggleState::Unselected => false,
|
||||
ToggleState::Indeterminate => { return; },
|
||||
};
|
||||
|
||||
update_settings_file::<VimModeSetting>(
|
||||
fs.clone(),
|
||||
cx,
|
||||
move |setting, _| *setting = Some(enabled),
|
||||
);
|
||||
}
|
||||
},
|
||||
))
|
||||
.child(render_telemetry_section(cx))
|
||||
.child(render_theme_section(&mut tab_index, cx))
|
||||
.child(render_base_keymap_section(&mut tab_index, cx))
|
||||
.child(render_vim_mode_switch(&mut tab_index, cx))
|
||||
.child(render_telemetry_section(&mut tab_index, cx))
|
||||
}
|
||||
|
|
|
@ -171,6 +171,7 @@ fn write_format_on_save(format_on_save: bool, cx: &mut App) {
|
|||
}
|
||||
|
||||
fn render_setting_import_button(
|
||||
tab_index: isize,
|
||||
label: SharedString,
|
||||
icon_name: IconName,
|
||||
action: &dyn Action,
|
||||
|
@ -182,6 +183,7 @@ fn render_setting_import_button(
|
|||
.full_width()
|
||||
.style(ButtonStyle::Outlined)
|
||||
.size(ButtonSize::Large)
|
||||
.tab_index(tab_index)
|
||||
.child(
|
||||
h_flex()
|
||||
.w_full()
|
||||
|
@ -214,7 +216,7 @@ fn render_setting_import_button(
|
|||
)
|
||||
}
|
||||
|
||||
fn render_import_settings_section(cx: &App) -> impl IntoElement {
|
||||
fn render_import_settings_section(tab_index: &mut isize, cx: &App) -> impl IntoElement {
|
||||
let import_state = SettingsImportState::global(cx);
|
||||
let imports: [(SharedString, IconName, &dyn Action, bool); 2] = [
|
||||
(
|
||||
|
@ -232,7 +234,8 @@ fn render_import_settings_section(cx: &App) -> impl IntoElement {
|
|||
];
|
||||
|
||||
let [vscode, cursor] = imports.map(|(label, icon_name, action, imported)| {
|
||||
render_setting_import_button(label, icon_name, action, imported)
|
||||
*tab_index += 1;
|
||||
render_setting_import_button(*tab_index - 1, label, icon_name, action, imported)
|
||||
});
|
||||
|
||||
v_flex()
|
||||
|
@ -248,7 +251,11 @@ fn render_import_settings_section(cx: &App) -> impl IntoElement {
|
|||
.child(h_flex().w_full().gap_4().child(vscode).child(cursor))
|
||||
}
|
||||
|
||||
fn render_font_customization_section(window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
fn render_font_customization_section(
|
||||
tab_index: &mut isize,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> impl IntoElement {
|
||||
let theme_settings = ThemeSettings::get_global(cx);
|
||||
let ui_font_size = theme_settings.ui_font_size(cx);
|
||||
let ui_font_family = theme_settings.ui_font.family.clone();
|
||||
|
@ -294,6 +301,10 @@ fn render_font_customization_section(window: &mut Window, cx: &mut App) -> impl
|
|||
.style(ButtonStyle::Outlined)
|
||||
.size(ButtonSize::Medium)
|
||||
.full_width()
|
||||
.tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index - 1
|
||||
})
|
||||
.child(
|
||||
h_flex()
|
||||
.w_full()
|
||||
|
@ -325,7 +336,11 @@ fn render_font_customization_section(window: &mut Window, cx: &mut App) -> impl
|
|||
write_ui_font_size(ui_font_size + px(1.), cx);
|
||||
},
|
||||
)
|
||||
.style(ui::NumericStepperStyle::Outlined),
|
||||
.style(ui::NumericStepperStyle::Outlined)
|
||||
.tab_index({
|
||||
*tab_index += 2;
|
||||
*tab_index - 2
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
@ -350,6 +365,10 @@ fn render_font_customization_section(window: &mut Window, cx: &mut App) -> impl
|
|||
.style(ButtonStyle::Outlined)
|
||||
.size(ButtonSize::Medium)
|
||||
.full_width()
|
||||
.tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index - 1
|
||||
})
|
||||
.child(
|
||||
h_flex()
|
||||
.w_full()
|
||||
|
@ -381,7 +400,11 @@ fn render_font_customization_section(window: &mut Window, cx: &mut App) -> impl
|
|||
write_buffer_font_size(buffer_font_size + px(1.), cx);
|
||||
},
|
||||
)
|
||||
.style(ui::NumericStepperStyle::Outlined),
|
||||
.style(ui::NumericStepperStyle::Outlined)
|
||||
.tab_index({
|
||||
*tab_index += 2;
|
||||
*tab_index - 2
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
@ -556,13 +579,17 @@ fn font_picker(
|
|||
.max_height(Some(rems(20.).into()))
|
||||
}
|
||||
|
||||
fn render_popular_settings_section(window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
fn render_popular_settings_section(
|
||||
tab_index: &mut isize,
|
||||
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(render_font_customization_section(tab_index, window, cx))
|
||||
.child(
|
||||
SwitchField::new(
|
||||
"onboarding-font-ligatures",
|
||||
|
@ -577,47 +604,69 @@ fn render_popular_settings_section(window: &mut Window, cx: &mut App) -> impl In
|
|||
write_font_ligatures(toggle_state == &ToggleState::Selected, cx);
|
||||
},
|
||||
)
|
||||
.tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index - 1
|
||||
})
|
||||
.tooltip(Tooltip::text(LIGATURE_TOOLTIP)),
|
||||
)
|
||||
.child(SwitchField::new(
|
||||
"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_format_on_save(toggle_state == &ToggleState::Selected, cx);
|
||||
},
|
||||
))
|
||||
.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);
|
||||
},
|
||||
))
|
||||
.child(
|
||||
SwitchField::new(
|
||||
"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_format_on_save(toggle_state == &ToggleState::Selected, cx);
|
||||
},
|
||||
)
|
||||
.tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index - 1
|
||||
}),
|
||||
)
|
||||
.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);
|
||||
},
|
||||
)
|
||||
.tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index - 1
|
||||
}),
|
||||
)
|
||||
.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);
|
||||
},
|
||||
)
|
||||
.tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index - 1
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.items_start()
|
||||
|
@ -648,6 +697,7 @@ fn render_popular_settings_section(window: &mut Window, cx: &mut App) -> impl In
|
|||
ShowMinimap::Always => 1,
|
||||
ShowMinimap::Never => 2,
|
||||
})
|
||||
.tab_index(tab_index)
|
||||
.style(ToggleButtonGroupStyle::Outlined)
|
||||
.button_width(ui::rems_from_px(64.)),
|
||||
),
|
||||
|
@ -655,8 +705,9 @@ fn render_popular_settings_section(window: &mut Window, cx: &mut App) -> impl In
|
|||
}
|
||||
|
||||
pub(crate) fn render_editing_page(window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let mut tab_index = 0;
|
||||
v_flex()
|
||||
.gap_4()
|
||||
.child(render_import_settings_section(cx))
|
||||
.child(render_popular_settings_section(window, cx))
|
||||
.child(render_import_settings_section(&mut tab_index, cx))
|
||||
.child(render_popular_settings_section(&mut tab_index, window, cx))
|
||||
}
|
||||
|
|
|
@ -75,6 +75,8 @@ actions!(
|
|||
ActivateEditingPage,
|
||||
/// Activates the AI Setup page.
|
||||
ActivateAISetupPage,
|
||||
/// Finish the onboarding process.
|
||||
Finish,
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -261,40 +263,6 @@ impl Onboarding {
|
|||
cx.emit(ItemEvent::UpdateTab);
|
||||
}
|
||||
|
||||
fn go_to_welcome_page(&self, cx: &mut App) {
|
||||
with_active_or_new_workspace(cx, |workspace, window, cx| {
|
||||
let Some((onboarding_id, onboarding_idx)) = workspace
|
||||
.active_pane()
|
||||
.read(cx)
|
||||
.items()
|
||||
.enumerate()
|
||||
.find_map(|(idx, item)| {
|
||||
let _ = item.downcast::<Onboarding>()?;
|
||||
Some((item.item_id(), idx))
|
||||
})
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
workspace.active_pane().update(cx, |pane, cx| {
|
||||
// Get the index here to get around the borrow checker
|
||||
let idx = pane.items().enumerate().find_map(|(idx, item)| {
|
||||
let _ = item.downcast::<WelcomePage>()?;
|
||||
Some(idx)
|
||||
});
|
||||
|
||||
if let Some(idx) = idx {
|
||||
pane.activate_item(idx, true, true, window, cx);
|
||||
} else {
|
||||
let item = Box::new(WelcomePage::new(window, cx));
|
||||
pane.add_item(item, true, true, Some(onboarding_idx), window, cx);
|
||||
}
|
||||
|
||||
pane.remove_item(onboarding_id, false, false, window, cx);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn render_nav_buttons(
|
||||
&mut self,
|
||||
window: &mut Window,
|
||||
|
@ -401,6 +369,13 @@ impl Onboarding {
|
|||
.children(self.render_nav_buttons(window, cx)),
|
||||
)
|
||||
.map(|this| {
|
||||
let keybinding = KeyBinding::for_action_in(
|
||||
&Finish,
|
||||
&self.focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
.map(|kb| kb.size(rems_from_px(12.)));
|
||||
if ai_setup_page {
|
||||
this.child(
|
||||
ButtonLike::new("start_building")
|
||||
|
@ -412,23 +387,37 @@ impl Onboarding {
|
|||
.w_full()
|
||||
.justify_between()
|
||||
.child(Label::new("Start Building"))
|
||||
.child(
|
||||
Icon::new(IconName::Check)
|
||||
.size(IconSize::Small),
|
||||
),
|
||||
.child(keybinding.map_or_else(
|
||||
|| {
|
||||
Icon::new(IconName::Check)
|
||||
.size(IconSize::Small)
|
||||
.into_any_element()
|
||||
},
|
||||
IntoElement::into_any_element,
|
||||
)),
|
||||
)
|
||||
.on_click(cx.listener(|this, _, _, cx| {
|
||||
this.go_to_welcome_page(cx);
|
||||
})),
|
||||
.on_click(|_, window, cx| {
|
||||
window.dispatch_action(Finish.boxed_clone(), cx);
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
this.child(
|
||||
ButtonLike::new("skip_all")
|
||||
.size(ButtonSize::Medium)
|
||||
.child(Label::new("Skip All").ml_1())
|
||||
.on_click(cx.listener(|this, _, _, cx| {
|
||||
this.go_to_welcome_page(cx);
|
||||
})),
|
||||
.child(
|
||||
h_flex()
|
||||
.ml_1()
|
||||
.w_full()
|
||||
.justify_between()
|
||||
.child(Label::new("Skip All"))
|
||||
.child(keybinding.map_or_else(
|
||||
|| gpui::Empty.into_any_element(),
|
||||
IntoElement::into_any_element,
|
||||
)),
|
||||
)
|
||||
.on_click(|_, window, cx| {
|
||||
window.dispatch_action(Finish.boxed_clone(), cx);
|
||||
}),
|
||||
)
|
||||
}
|
||||
}),
|
||||
|
@ -464,17 +453,23 @@ impl Onboarding {
|
|||
|
||||
fn render_page(&mut self, window: &mut Window, cx: &mut Context<Self>) -> AnyElement {
|
||||
match self.selected_page {
|
||||
SelectedPage::Basics => {
|
||||
crate::basics_page::render_basics_page(window, cx).into_any_element()
|
||||
}
|
||||
SelectedPage::Basics => crate::basics_page::render_basics_page(cx).into_any_element(),
|
||||
SelectedPage::Editing => {
|
||||
crate::editing_page::render_editing_page(window, cx).into_any_element()
|
||||
}
|
||||
SelectedPage::AiSetup => {
|
||||
crate::ai_setup_page::render_ai_setup_page(&self, window, cx).into_any_element()
|
||||
}
|
||||
SelectedPage::AiSetup => crate::ai_setup_page::render_ai_setup_page(
|
||||
self.workspace.clone(),
|
||||
self.user_store.clone(),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
.into_any_element(),
|
||||
}
|
||||
}
|
||||
|
||||
fn on_finish(_: &Finish, _: &mut Window, cx: &mut App) {
|
||||
go_to_welcome_page(cx);
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for Onboarding {
|
||||
|
@ -484,11 +479,13 @@ impl Render for Onboarding {
|
|||
.key_context({
|
||||
let mut ctx = KeyContext::new_with_defaults();
|
||||
ctx.add("Onboarding");
|
||||
ctx.add("menu");
|
||||
ctx
|
||||
})
|
||||
.track_focus(&self.focus_handle)
|
||||
.size_full()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.on_action(Self::on_finish)
|
||||
.on_action(cx.listener(|this, _: &ActivateBasicsPage, _, cx| {
|
||||
this.set_page(SelectedPage::Basics, cx);
|
||||
}))
|
||||
|
@ -498,6 +495,14 @@ impl Render for Onboarding {
|
|||
.on_action(cx.listener(|this, _: &ActivateAISetupPage, _, cx| {
|
||||
this.set_page(SelectedPage::AiSetup, cx);
|
||||
}))
|
||||
.on_action(cx.listener(|_, _: &menu::SelectNext, window, cx| {
|
||||
window.focus_next();
|
||||
cx.notify();
|
||||
}))
|
||||
.on_action(cx.listener(|_, _: &menu::SelectPrevious, window, cx| {
|
||||
window.focus_prev();
|
||||
cx.notify();
|
||||
}))
|
||||
.child(
|
||||
h_flex()
|
||||
.max_w(rems_from_px(1100.))
|
||||
|
@ -561,6 +566,40 @@ impl Item for Onboarding {
|
|||
}
|
||||
}
|
||||
|
||||
fn go_to_welcome_page(cx: &mut App) {
|
||||
with_active_or_new_workspace(cx, |workspace, window, cx| {
|
||||
let Some((onboarding_id, onboarding_idx)) = workspace
|
||||
.active_pane()
|
||||
.read(cx)
|
||||
.items()
|
||||
.enumerate()
|
||||
.find_map(|(idx, item)| {
|
||||
let _ = item.downcast::<Onboarding>()?;
|
||||
Some((item.item_id(), idx))
|
||||
})
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
workspace.active_pane().update(cx, |pane, cx| {
|
||||
// Get the index here to get around the borrow checker
|
||||
let idx = pane.items().enumerate().find_map(|(idx, item)| {
|
||||
let _ = item.downcast::<WelcomePage>()?;
|
||||
Some(idx)
|
||||
});
|
||||
|
||||
if let Some(idx) = idx {
|
||||
pane.activate_item(idx, true, true, window, cx);
|
||||
} else {
|
||||
let item = Box::new(WelcomePage::new(window, cx));
|
||||
pane.add_item(item, true, true, Some(onboarding_idx), window, cx);
|
||||
}
|
||||
|
||||
pane.remove_item(onboarding_id, false, false, window, cx);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
pub async fn handle_import_vscode_settings(
|
||||
workspace: WeakEntity<Workspace>,
|
||||
source: VsCodeSettingsSource,
|
||||
|
|
|
@ -412,6 +412,7 @@ where
|
|||
size: ToggleButtonGroupSize,
|
||||
button_width: Rems,
|
||||
selected_index: usize,
|
||||
tab_index: Option<isize>,
|
||||
}
|
||||
|
||||
impl<T: ButtonBuilder, const COLS: usize> ToggleButtonGroup<T, COLS> {
|
||||
|
@ -423,6 +424,7 @@ impl<T: ButtonBuilder, const COLS: usize> ToggleButtonGroup<T, COLS> {
|
|||
size: ToggleButtonGroupSize::Default,
|
||||
button_width: rems_from_px(100.),
|
||||
selected_index: 0,
|
||||
tab_index: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -436,6 +438,7 @@ impl<T: ButtonBuilder, const COLS: usize> ToggleButtonGroup<T, COLS, 2> {
|
|||
size: ToggleButtonGroupSize::Default,
|
||||
button_width: rems_from_px(100.),
|
||||
selected_index: 0,
|
||||
tab_index: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -460,6 +463,15 @@ impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> ToggleButtonGroup<T
|
|||
self.selected_index = index;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the tab index for the toggle button group.
|
||||
/// The tab index is set to the initial value provided, then the
|
||||
/// value is incremented by the number of buttons in the group.
|
||||
pub fn tab_index(mut self, tab_index: &mut isize) -> Self {
|
||||
self.tab_index = Some(*tab_index);
|
||||
*tab_index += (COLS * ROWS) as isize;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> RenderOnce
|
||||
|
@ -479,6 +491,9 @@ impl<T: ButtonBuilder, const COLS: usize, const ROWS: usize> RenderOnce
|
|||
let entry_index = row_index * COLS + col_index;
|
||||
|
||||
ButtonLike::new((self.group_name, entry_index))
|
||||
.when_some(self.tab_index, |this, tab_index| {
|
||||
this.tab_index(tab_index + entry_index as isize)
|
||||
})
|
||||
.when(entry_index == self.selected_index || selected, |this| {
|
||||
this.toggle_state(true)
|
||||
.selected_style(ButtonStyle::Tinted(TintColor::Accent))
|
||||
|
|
|
@ -19,6 +19,7 @@ pub struct NumericStepper {
|
|||
/// Whether to reserve space for the reset button.
|
||||
reserve_space_for_reset: bool,
|
||||
on_reset: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
|
||||
tab_index: Option<isize>,
|
||||
}
|
||||
|
||||
impl NumericStepper {
|
||||
|
@ -36,6 +37,7 @@ impl NumericStepper {
|
|||
on_increment: Box::new(on_increment),
|
||||
reserve_space_for_reset: false,
|
||||
on_reset: None,
|
||||
tab_index: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,6 +58,11 @@ impl NumericStepper {
|
|||
self.on_reset = Some(Box::new(on_reset));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn tab_index(mut self, tab_index: isize) -> Self {
|
||||
self.tab_index = Some(tab_index);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for NumericStepper {
|
||||
|
@ -64,6 +71,7 @@ impl RenderOnce for NumericStepper {
|
|||
let icon_size = IconSize::Small;
|
||||
|
||||
let is_outlined = matches!(self.style, NumericStepperStyle::Outlined);
|
||||
let mut tab_index = self.tab_index;
|
||||
|
||||
h_flex()
|
||||
.id(self.id)
|
||||
|
@ -74,6 +82,10 @@ impl RenderOnce for NumericStepper {
|
|||
IconButton::new("reset", IconName::RotateCcw)
|
||||
.shape(shape)
|
||||
.icon_size(icon_size)
|
||||
.when_some(tab_index.as_mut(), |this, tab_index| {
|
||||
*tab_index += 1;
|
||||
this.tab_index(*tab_index - 1)
|
||||
})
|
||||
.on_click(on_reset),
|
||||
)
|
||||
} else if self.reserve_space_for_reset {
|
||||
|
@ -113,6 +125,12 @@ impl RenderOnce for NumericStepper {
|
|||
.border_r_1()
|
||||
.border_color(cx.theme().colors().border_variant)
|
||||
.child(Icon::new(IconName::Dash).size(IconSize::Small))
|
||||
.when_some(tab_index.as_mut(), |this, tab_index| {
|
||||
*tab_index += 1;
|
||||
this.tab_index(*tab_index - 1).focus(|style| {
|
||||
style.bg(cx.theme().colors().element_hover)
|
||||
})
|
||||
})
|
||||
.on_click(self.on_decrement),
|
||||
)
|
||||
} else {
|
||||
|
@ -120,6 +138,10 @@ impl RenderOnce for NumericStepper {
|
|||
IconButton::new("decrement", IconName::Dash)
|
||||
.shape(shape)
|
||||
.icon_size(icon_size)
|
||||
.when_some(tab_index.as_mut(), |this, tab_index| {
|
||||
*tab_index += 1;
|
||||
this.tab_index(*tab_index - 1)
|
||||
})
|
||||
.on_click(self.on_decrement),
|
||||
)
|
||||
}
|
||||
|
@ -137,6 +159,12 @@ impl RenderOnce for NumericStepper {
|
|||
.border_l_1()
|
||||
.border_color(cx.theme().colors().border_variant)
|
||||
.child(Icon::new(IconName::Plus).size(IconSize::Small))
|
||||
.when_some(tab_index.as_mut(), |this, tab_index| {
|
||||
*tab_index += 1;
|
||||
this.tab_index(*tab_index - 1).focus(|style| {
|
||||
style.bg(cx.theme().colors().element_hover)
|
||||
})
|
||||
})
|
||||
.on_click(self.on_increment),
|
||||
)
|
||||
} else {
|
||||
|
@ -144,6 +172,10 @@ impl RenderOnce for NumericStepper {
|
|||
IconButton::new("increment", IconName::Dash)
|
||||
.shape(shape)
|
||||
.icon_size(icon_size)
|
||||
.when_some(tab_index.as_mut(), |this, tab_index| {
|
||||
*tab_index += 1;
|
||||
this.tab_index(*tab_index - 1)
|
||||
})
|
||||
.on_click(self.on_increment),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -424,6 +424,7 @@ pub struct Switch {
|
|||
label: Option<SharedString>,
|
||||
key_binding: Option<KeyBinding>,
|
||||
color: SwitchColor,
|
||||
tab_index: Option<isize>,
|
||||
}
|
||||
|
||||
impl Switch {
|
||||
|
@ -437,6 +438,7 @@ impl Switch {
|
|||
label: None,
|
||||
key_binding: None,
|
||||
color: SwitchColor::default(),
|
||||
tab_index: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -472,6 +474,11 @@ impl Switch {
|
|||
self.key_binding = key_binding.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn tab_index(mut self, tab_index: impl Into<isize>) -> Self {
|
||||
self.tab_index = Some(tab_index.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Switch {
|
||||
|
@ -501,6 +508,20 @@ impl RenderOnce for Switch {
|
|||
.w(DynamicSpacing::Base32.rems(cx))
|
||||
.h(DynamicSpacing::Base20.rems(cx))
|
||||
.group(group_id.clone())
|
||||
.border_1()
|
||||
.p(px(1.0))
|
||||
.border_color(cx.theme().colors().border_transparent)
|
||||
.rounded_full()
|
||||
.id((self.id.clone(), "switch"))
|
||||
.when_some(
|
||||
self.tab_index.filter(|_| !self.disabled),
|
||||
|this, tab_index| {
|
||||
this.tab_index(tab_index).focus(|mut style| {
|
||||
style.border_color = Some(cx.theme().colors().border_focused);
|
||||
style
|
||||
})
|
||||
},
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.when(is_on, |on| on.justify_end())
|
||||
|
@ -572,6 +593,7 @@ pub struct SwitchField {
|
|||
disabled: bool,
|
||||
color: SwitchColor,
|
||||
tooltip: Option<Rc<dyn Fn(&mut Window, &mut App) -> AnyView>>,
|
||||
tab_index: Option<isize>,
|
||||
}
|
||||
|
||||
impl SwitchField {
|
||||
|
@ -591,6 +613,7 @@ impl SwitchField {
|
|||
disabled: false,
|
||||
color: SwitchColor::Accent,
|
||||
tooltip: None,
|
||||
tab_index: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -615,14 +638,33 @@ impl SwitchField {
|
|||
self.tooltip = Some(Rc::new(tooltip));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn tab_index(mut self, tab_index: isize) -> Self {
|
||||
self.tab_index = Some(tab_index);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for SwitchField {
|
||||
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
|
||||
let tooltip = self.tooltip;
|
||||
let tooltip = self.tooltip.map(|tooltip_fn| {
|
||||
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_fn.clone();
|
||||
move |window, cx| tooltip(window, cx)
|
||||
}),
|
||||
)
|
||||
});
|
||||
|
||||
h_flex()
|
||||
.id(SharedString::from(format!("{}-container", self.id)))
|
||||
.id((self.id.clone(), "container"))
|
||||
.when(!self.disabled, |this| {
|
||||
this.hover(|this| this.cursor_pointer())
|
||||
})
|
||||
|
@ -630,25 +672,11 @@ impl RenderOnce for SwitchField {
|
|||
.gap_4()
|
||||
.justify_between()
|
||||
.flex_wrap()
|
||||
.child(match (&self.description, &tooltip) {
|
||||
.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(tooltip)
|
||||
.child(Label::new(description.clone()).color(Color::Muted))
|
||||
.into_any_element(),
|
||||
(Some(description), None) => v_flex()
|
||||
|
@ -657,35 +685,23 @@ impl RenderOnce for SwitchField {
|
|||
.child(Label::new(self.label.clone()))
|
||||
.child(Label::new(description.clone()).color(Color::Muted))
|
||||
.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, Some(tooltip)) => tooltip.into_any_element(),
|
||||
(None, None) => Label::new(self.label.clone()).into_any_element(),
|
||||
})
|
||||
.child(
|
||||
Switch::new(
|
||||
SharedString::from(format!("{}-switch", self.id)),
|
||||
self.toggle_state,
|
||||
)
|
||||
.color(self.color)
|
||||
.disabled(self.disabled)
|
||||
.on_click({
|
||||
let on_click = self.on_click.clone();
|
||||
move |state, window, cx| {
|
||||
(on_click)(state, window, cx);
|
||||
}
|
||||
}),
|
||||
Switch::new((self.id.clone(), "switch"), self.toggle_state)
|
||||
.color(self.color)
|
||||
.disabled(self.disabled)
|
||||
.when_some(
|
||||
self.tab_index.filter(|_| !self.disabled),
|
||||
|this, tab_index| this.tab_index(tab_index),
|
||||
)
|
||||
.on_click({
|
||||
let on_click = self.on_click.clone();
|
||||
move |state, window, cx| {
|
||||
(on_click)(state, window, cx);
|
||||
}
|
||||
}),
|
||||
)
|
||||
.when(!self.disabled, |this| {
|
||||
this.on_click({
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue