gwen: Add background image picker to onboarding
Add a Background section to the welcome page: a file picker writes background.path into user settings, a Clear button removes it, and the chosen image is previewed in place. No default background is set; fit, position and opacity remain in the Settings page.
This commit is contained in:
1 parent
4e15e1e0ed
commit
faa11dea4c
1 file changed
+96
-2
@@ -1,3 +1,4 @@
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -5,7 +6,10 @@ use client::{Client, TelemetrySettings, UserStore, zed_urls};
|
||||
use cloud_api_types::Plan;
|
||||
use collections::HashMap;
|
||||
use fs::Fs;
|
||||
use gpui::{Action, Animation, AnimationExt, App, Entity, IntoElement, TaskExt, pulsating_between};
|
||||
use gpui::{
|
||||
Action, Animation, AnimationExt, App, Entity, ImageSource, IntoElement, ObjectFit,
|
||||
PathPromptOptions, Resource, TaskExt, img, pulsating_between,
|
||||
};
|
||||
use project::agent_server_store::AllAgentServersSettings;
|
||||
use project::project_settings::ProjectSettings;
|
||||
use project::{AgentRegistryStore, RegistryAgent};
|
||||
@@ -15,7 +19,7 @@ use settings::{
|
||||
use theme::{Appearance, SystemAppearance, ThemeRegistry};
|
||||
use theme_settings::{ThemeAppearanceMode, ThemeName, ThemeSelection, ThemeSettings};
|
||||
use ui::{
|
||||
AgentSetupButton, Divider, StatefulInteractiveElement, SwitchField, TintColor,
|
||||
AgentSetupButton, Button, Divider, StatefulInteractiveElement, SwitchField, TintColor,
|
||||
ToggleButtonGroup, ToggleButtonGroupSize, ToggleButtonSimple, ToggleButtonWithIcon, Tooltip,
|
||||
prelude::*,
|
||||
};
|
||||
@@ -242,6 +246,95 @@ fn render_theme_section(tab_index: &mut isize, cx: &mut App) -> impl IntoElement
|
||||
}
|
||||
}
|
||||
|
||||
fn render_background_section(tab_index: &mut isize, cx: &mut App) -> impl IntoElement {
|
||||
let current_path = cx
|
||||
.try_global::<SettingsStore>()
|
||||
.and_then(|store| store.merged_settings().background.clone())
|
||||
.and_then(|background| background.path)
|
||||
.filter(|path| !path.is_empty());
|
||||
let has_background = current_path.is_some();
|
||||
let colors = cx.theme().colors();
|
||||
let fs = <dyn Fs>::global(cx);
|
||||
|
||||
let select_button = {
|
||||
let fs = fs.clone();
|
||||
Button::new("onboarding-background-select", "Select Image…")
|
||||
.tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index - 1
|
||||
})
|
||||
.on_click(move |_, _window, cx| {
|
||||
let receiver = cx.prompt_for_paths(PathPromptOptions {
|
||||
files: true,
|
||||
directories: false,
|
||||
multiple: false,
|
||||
prompt: None,
|
||||
});
|
||||
let fs = fs.clone();
|
||||
cx.spawn(async move |cx| {
|
||||
let Ok(Ok(Some(paths))) = receiver.await else {
|
||||
return;
|
||||
};
|
||||
let Some(path) = paths.into_iter().next() else {
|
||||
return;
|
||||
};
|
||||
let path = path.to_string_lossy().to_string();
|
||||
cx.update(|cx| {
|
||||
update_settings_file(fs, cx, move |settings, _cx| {
|
||||
settings.background.get_or_insert_default().path = Some(path);
|
||||
});
|
||||
telemetry::event!("Welcome Background Changed", set = true);
|
||||
});
|
||||
})
|
||||
.detach();
|
||||
})
|
||||
};
|
||||
|
||||
let clear_button = {
|
||||
let fs = fs.clone();
|
||||
Button::new("onboarding-background-clear", "Clear")
|
||||
.tab_index({
|
||||
*tab_index += 1;
|
||||
*tab_index - 1
|
||||
})
|
||||
.on_click(move |_, _, cx| {
|
||||
update_settings_file(fs.clone(), cx, |settings, _cx| {
|
||||
settings.background.get_or_insert_default().path = None;
|
||||
});
|
||||
telemetry::event!("Welcome Background Changed", set = false);
|
||||
})
|
||||
};
|
||||
|
||||
let preview = match current_path {
|
||||
Some(path) => img(ImageSource::Resource(Resource::Path(Arc::from(Path::new(&path)))))
|
||||
.w_full()
|
||||
.h(rems_from_px(120.0_f32))
|
||||
.object_fit(ObjectFit::Cover)
|
||||
.rounded_md()
|
||||
.border_1()
|
||||
.border_color(colors.border)
|
||||
.into_any_element(),
|
||||
None => Label::new("No background image yet — pick one to show it behind the editor.")
|
||||
.color(Color::Muted)
|
||||
.into_any_element(),
|
||||
};
|
||||
|
||||
v_flex()
|
||||
.gap_2()
|
||||
.child(
|
||||
h_flex()
|
||||
.justify_between()
|
||||
.child(Label::new("Background"))
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.child(select_button)
|
||||
.when(has_background, |this| this.child(clear_button)),
|
||||
),
|
||||
)
|
||||
.child(preview)
|
||||
}
|
||||
|
||||
fn render_telemetry_section(tab_index: &mut isize, cx: &App) -> impl IntoElement {
|
||||
let fs = <dyn Fs>::global(cx);
|
||||
|
||||
@@ -720,6 +813,7 @@ pub(crate) fn render_basics_page(user_store: &Entity<UserStore>, cx: &mut App) -
|
||||
.id("basics-page")
|
||||
.gap_6()
|
||||
.child(render_theme_section(&mut tab_index, cx))
|
||||
.child(render_background_section(&mut tab_index, cx))
|
||||
.child(render_base_keymap_section(&mut tab_index, cx))
|
||||
.child(render_ai_section(user_store, cx))
|
||||
.child(render_import_settings_section(&mut tab_index, cx))
|
||||
|
||||
Reference in new issue
Block a user