add settings dialog
Rust / build (macos-latest, stable) (push) Waiting to run
Rust / build (ubuntu-latest, stable) (push) Waiting to run
Rust / build (windows-latest, stable) (push) Waiting to run
Rust / build (macos-latest, stable) (pull_request) Waiting to run
Rust / build (ubuntu-latest, stable) (pull_request) Waiting to run
Rust / build (windows-latest, stable) (pull_request) Waiting to run
Rust / build (macos-latest, stable) (push) Waiting to run
Rust / build (ubuntu-latest, stable) (push) Waiting to run
Rust / build (windows-latest, stable) (push) Waiting to run
Rust / build (macos-latest, stable) (pull_request) Waiting to run
Rust / build (ubuntu-latest, stable) (pull_request) Waiting to run
Rust / build (windows-latest, stable) (pull_request) Waiting to run
This commit is contained in:
Generated
+8
-8
@@ -3059,7 +3059,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-native-core"
|
name = "gpui-native-core"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://git.reya.info/reya/gpui-native-kit?rev=ccd8997801186a33d4ed299d5bc9446c0f015781#ccd8997801186a33d4ed299d5bc9446c0f015781"
|
source = "git+https://git.reya.info/reya/gpui-native-kit?rev=9678a7676f4c70f91f04b6ab0ad68148659c2558#9678a7676f4c70f91f04b6ab0ad68148659c2558"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cpal",
|
"cpal",
|
||||||
"flume 0.12.0",
|
"flume 0.12.0",
|
||||||
@@ -3074,7 +3074,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-native-data"
|
name = "gpui-native-data"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://git.reya.info/reya/gpui-native-kit?rev=ccd8997801186a33d4ed299d5bc9446c0f015781#ccd8997801186a33d4ed299d5bc9446c0f015781"
|
source = "git+https://git.reya.info/reya/gpui-native-kit?rev=9678a7676f4c70f91f04b6ab0ad68148659c2558#9678a7676f4c70f91f04b6ab0ad68148659c2558"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gpui",
|
"gpui",
|
||||||
"gpui-native-core",
|
"gpui-native-core",
|
||||||
@@ -3086,7 +3086,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-native-editor"
|
name = "gpui-native-editor"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://git.reya.info/reya/gpui-native-kit?rev=ccd8997801186a33d4ed299d5bc9446c0f015781#ccd8997801186a33d4ed299d5bc9446c0f015781"
|
source = "git+https://git.reya.info/reya/gpui-native-kit?rev=9678a7676f4c70f91f04b6ab0ad68148659c2558#9678a7676f4c70f91f04b6ab0ad68148659c2558"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gpui",
|
"gpui",
|
||||||
"gpui-native-core",
|
"gpui-native-core",
|
||||||
@@ -3105,7 +3105,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-native-icons"
|
name = "gpui-native-icons"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://git.reya.info/reya/gpui-native-kit?rev=ccd8997801186a33d4ed299d5bc9446c0f015781#ccd8997801186a33d4ed299d5bc9446c0f015781"
|
source = "git+https://git.reya.info/reya/gpui-native-kit?rev=9678a7676f4c70f91f04b6ab0ad68148659c2558#9678a7676f4c70f91f04b6ab0ad68148659c2558"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"gpui",
|
"gpui",
|
||||||
@@ -3114,7 +3114,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-native-kit"
|
name = "gpui-native-kit"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://git.reya.info/reya/gpui-native-kit?rev=ccd8997801186a33d4ed299d5bc9446c0f015781#ccd8997801186a33d4ed299d5bc9446c0f015781"
|
source = "git+https://git.reya.info/reya/gpui-native-kit?rev=9678a7676f4c70f91f04b6ab0ad68148659c2558#9678a7676f4c70f91f04b6ab0ad68148659c2558"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gpui",
|
"gpui",
|
||||||
"gpui-native-core",
|
"gpui-native-core",
|
||||||
@@ -3128,7 +3128,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-native-layout"
|
name = "gpui-native-layout"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://git.reya.info/reya/gpui-native-kit?rev=ccd8997801186a33d4ed299d5bc9446c0f015781#ccd8997801186a33d4ed299d5bc9446c0f015781"
|
source = "git+https://git.reya.info/reya/gpui-native-kit?rev=9678a7676f4c70f91f04b6ab0ad68148659c2558#9678a7676f4c70f91f04b6ab0ad68148659c2558"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gpui",
|
"gpui",
|
||||||
"gpui-native-core",
|
"gpui-native-core",
|
||||||
@@ -3141,7 +3141,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-native-theme"
|
name = "gpui-native-theme"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://git.reya.info/reya/gpui-native-kit?rev=ccd8997801186a33d4ed299d5bc9446c0f015781#ccd8997801186a33d4ed299d5bc9446c0f015781"
|
source = "git+https://git.reya.info/reya/gpui-native-kit?rev=9678a7676f4c70f91f04b6ab0ad68148659c2558#9678a7676f4c70f91f04b6ab0ad68148659c2558"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-channel",
|
"async-channel",
|
||||||
@@ -3162,7 +3162,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gpui-native-utils"
|
name = "gpui-native-utils"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://git.reya.info/reya/gpui-native-kit?rev=ccd8997801186a33d4ed299d5bc9446c0f015781#ccd8997801186a33d4ed299d5bc9446c0f015781"
|
source = "git+https://git.reya.info/reya/gpui-native-kit?rev=9678a7676f4c70f91f04b6ab0ad68148659c2558#9678a7676f4c70f91f04b6ab0ad68148659c2558"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gpui",
|
"gpui",
|
||||||
"gpui-native-theme",
|
"gpui-native-theme",
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@ gpui = { git = "https://github.com/zed-industries/zed", rev = "f8cfc7ed9e57503de
|
|||||||
gpui_platform = { git = "https://github.com/zed-industries/zed", rev = "f8cfc7ed9e57503def9479138894f3139f5400cc", features = ["font-kit"] }
|
gpui_platform = { git = "https://github.com/zed-industries/zed", rev = "f8cfc7ed9e57503def9479138894f3139f5400cc", features = ["font-kit"] }
|
||||||
reqwest_client = { git = "https://github.com/zed-industries/zed", rev = "f8cfc7ed9e57503def9479138894f3139f5400cc" }
|
reqwest_client = { git = "https://github.com/zed-industries/zed", rev = "f8cfc7ed9e57503def9479138894f3139f5400cc" }
|
||||||
|
|
||||||
gpui-native-kit = { git = "https://git.reya.info/reya/gpui-native-kit", rev = "ccd8997801186a33d4ed299d5bc9446c0f015781", features = ["layout", "editor"] }
|
gpui-native-kit = { git = "https://git.reya.info/reya/gpui-native-kit", rev = "9678a7676f4c70f91f04b6ab0ad68148659c2558", features = ["layout", "editor"] }
|
||||||
|
|
||||||
settings = { path = "crates/settings" }
|
settings = { path = "crates/settings" }
|
||||||
signed_app = { path = "crates/signed_app" }
|
signed_app = { path = "crates/signed_app" }
|
||||||
|
|||||||
@@ -4,7 +4,12 @@ use settings::{AppearanceMode, Settings};
|
|||||||
|
|
||||||
/// Applies the persisted light/dark/system preference to the kit theme.
|
/// Applies the persisted light/dark/system preference to the kit theme.
|
||||||
pub fn apply_appearance(settings: &Settings, cx: &mut App) {
|
pub fn apply_appearance(settings: &Settings, cx: &mut App) {
|
||||||
match settings.appearance {
|
apply_appearance_mode(settings.appearance, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Applies one appearance mode to the kit theme.
|
||||||
|
pub fn apply_appearance_mode(mode: AppearanceMode, cx: &mut App) {
|
||||||
|
match mode {
|
||||||
AppearanceMode::System => follow_system(cx),
|
AppearanceMode::System => follow_system(cx),
|
||||||
AppearanceMode::Light => set_mode_override(Some(ThemeVariant::Light), cx),
|
AppearanceMode::Light => set_mode_override(Some(ThemeVariant::Light), cx),
|
||||||
AppearanceMode::Dark => set_mode_override(Some(ThemeVariant::Dark), cx),
|
AppearanceMode::Dark => set_mode_override(Some(ThemeVariant::Dark), cx),
|
||||||
|
|||||||
@@ -5,11 +5,12 @@ mod inbox;
|
|||||||
mod issues;
|
mod issues;
|
||||||
mod pull_requests;
|
mod pull_requests;
|
||||||
mod repo;
|
mod repo;
|
||||||
|
mod settings;
|
||||||
mod shared;
|
mod shared;
|
||||||
mod sidebar;
|
mod sidebar;
|
||||||
|
|
||||||
pub use app::AppRoot;
|
pub use app::AppRoot;
|
||||||
pub use app::helpers::apply_appearance;
|
pub use app::helpers::{apply_appearance, apply_appearance_mode};
|
||||||
|
|
||||||
use gpui::{App, AppContext as _, Entity, Window};
|
use gpui::{App, AppContext as _, Entity, Window};
|
||||||
|
|
||||||
|
|||||||
@@ -55,8 +55,10 @@ impl InitRepoDialog {
|
|||||||
let state = cx.new(|_| DialogProgress::default());
|
let state = cx.new(|_| DialogProgress::default());
|
||||||
let grasp_state = cx.new(|_| GraspServersState::new_default(&grasp_settings));
|
let grasp_state = cx.new(|_| GraspServersState::new_default(&grasp_settings));
|
||||||
|
|
||||||
let name_input = InputState::builder().text(default_name).build(cx);
|
let name_input = InputState::builder()
|
||||||
name_input.update(cx, |state, cx| state.set_read_only(true, cx));
|
.text(default_name)
|
||||||
|
.read_only()
|
||||||
|
.build(cx);
|
||||||
|
|
||||||
let desc_input = InputState::builder().multiline().build(cx);
|
let desc_input = InputState::builder().multiline().build(cx);
|
||||||
let relay_input = InputState::new(cx);
|
let relay_input = InputState::new(cx);
|
||||||
|
|||||||
@@ -0,0 +1,369 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use gpui::{
|
||||||
|
AnyElement, App, AppContext as _, Entity, IntoElement, ParentElement as _, PathPromptOptions,
|
||||||
|
SharedString, Styled as _, WeakEntity, Window, div, px,
|
||||||
|
};
|
||||||
|
use gpui_native_kit::button::Button;
|
||||||
|
use gpui_native_kit::common::{h_flex, v_flex};
|
||||||
|
use gpui_native_kit::icons::{Icon, IconRole};
|
||||||
|
use gpui_native_kit::input::{Input, InputState};
|
||||||
|
use gpui_native_kit::select::{Select, SelectOption};
|
||||||
|
use gpui_native_kit::theme::ActiveTheme as _;
|
||||||
|
use gpui_native_kit::tooltip::Tooltip;
|
||||||
|
use settings::{AppearanceMode, EventFetchingStrategy, SettingsStore};
|
||||||
|
|
||||||
|
use super::SettingsDialog;
|
||||||
|
use crate::repo::dialogs::grasp_servers::{normalize_server, server_host};
|
||||||
|
|
||||||
|
/// A settings row with the label and description on the left and the control on the right.
|
||||||
|
pub fn setting_row(
|
||||||
|
cx: &App,
|
||||||
|
title: impl Into<SharedString>,
|
||||||
|
description: impl Into<SharedString>,
|
||||||
|
control: impl IntoElement,
|
||||||
|
) -> AnyElement {
|
||||||
|
h_flex()
|
||||||
|
.w_full()
|
||||||
|
.justify_between()
|
||||||
|
.gap_4()
|
||||||
|
.child(
|
||||||
|
v_flex()
|
||||||
|
.flex_1()
|
||||||
|
.min_w_0()
|
||||||
|
.gap_1()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_sm()
|
||||||
|
.font_weight(gpui::FontWeight::SEMIBOLD)
|
||||||
|
.child(title.into()),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_xs()
|
||||||
|
.text_color(cx.theme().colors.muted_foreground)
|
||||||
|
.child(description.into()),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(div().w(px(200.)).flex_shrink_0().child(control))
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A full-width settings block with title and subtitle in one header.
|
||||||
|
pub fn setting_block(
|
||||||
|
cx: &App,
|
||||||
|
title: impl Into<SharedString>,
|
||||||
|
description: impl Into<SharedString>,
|
||||||
|
control: impl IntoElement,
|
||||||
|
) -> AnyElement {
|
||||||
|
v_flex()
|
||||||
|
.w_full()
|
||||||
|
.gap_3()
|
||||||
|
.child(
|
||||||
|
v_flex()
|
||||||
|
.gap_1()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_sm()
|
||||||
|
.font_weight(gpui::FontWeight::SEMIBOLD)
|
||||||
|
.child(title.into()),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_xs()
|
||||||
|
.text_color(cx.theme().colors.muted_foreground)
|
||||||
|
.child(description.into()),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(control)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn appearance_select(cx: &App) -> Select<AppearanceMode> {
|
||||||
|
let current = SettingsStore::global(cx).read(cx).settings().appearance;
|
||||||
|
Select::new("settings-appearance")
|
||||||
|
.options(vec![
|
||||||
|
SelectOption::new(AppearanceMode::System, "System"),
|
||||||
|
SelectOption::new(AppearanceMode::Light, "Light"),
|
||||||
|
SelectOption::new(AppearanceMode::Dark, "Dark"),
|
||||||
|
])
|
||||||
|
.selected(Some(current))
|
||||||
|
.on_change(|value, _, cx| {
|
||||||
|
let Some(mode) = value else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
SettingsStore::global(cx).update(cx, |store, cx| {
|
||||||
|
store.edit(|settings| settings.appearance = mode, cx);
|
||||||
|
});
|
||||||
|
crate::apply_appearance_mode(mode, cx);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn event_fetching_select(cx: &App) -> Select<EventFetchingStrategy> {
|
||||||
|
let current = SettingsStore::global(cx).read(cx).settings().event_fetching;
|
||||||
|
Select::new("settings-event-fetching")
|
||||||
|
.options(vec![
|
||||||
|
SelectOption::new(EventFetchingStrategy::Curated, "Curated"),
|
||||||
|
SelectOption::new(EventFetchingStrategy::Uncensored, "Uncensored"),
|
||||||
|
])
|
||||||
|
.selected(Some(current))
|
||||||
|
.on_change(|value, _, cx| {
|
||||||
|
let Some(strategy) = value else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
SettingsStore::global(cx).update(cx, |store, cx| {
|
||||||
|
store.edit(|settings| settings.event_fetching = strategy, cx);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn grasp_servers_editor(input: &Entity<InputState>, cx: &App) -> AnyElement {
|
||||||
|
let servers = SettingsStore::global(cx)
|
||||||
|
.read(cx)
|
||||||
|
.settings()
|
||||||
|
.grasp_servers
|
||||||
|
.default_servers
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
v_flex()
|
||||||
|
.gap_2()
|
||||||
|
.children(servers.iter().enumerate().map(|(ix, server)| {
|
||||||
|
let store = SettingsStore::global(cx);
|
||||||
|
h_flex()
|
||||||
|
.gap_1()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex_1()
|
||||||
|
.h_8()
|
||||||
|
.px_2()
|
||||||
|
.bg(cx.theme().colors.muted)
|
||||||
|
.text_color(cx.theme().colors.muted_foreground)
|
||||||
|
.text_sm()
|
||||||
|
.rounded(cx.theme().radius.control)
|
||||||
|
.child(display_server(server)),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new(format!("settings-remove-server:{ix}"))
|
||||||
|
.icon(Icon::named(IconRole::Close))
|
||||||
|
.variant(gpui_native_kit::button::ButtonVariant::Ghost)
|
||||||
|
.tooltip(Tooltip::text("Remove"))
|
||||||
|
.on_click({
|
||||||
|
let store = store.clone();
|
||||||
|
move |_, _, cx| {
|
||||||
|
store.update(cx, |store, cx| {
|
||||||
|
store.edit(
|
||||||
|
|settings| {
|
||||||
|
settings.grasp_servers.default_servers.remove(ix);
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
}))
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.gap_1()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex_1()
|
||||||
|
.child(Input::new("settings-grasp-input", input.clone())),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new("settings-add-server")
|
||||||
|
.icon(Icon::named(IconRole::Plus))
|
||||||
|
.variant(gpui_native_kit::button::ButtonVariant::Ghost)
|
||||||
|
.tooltip(Tooltip::text("Add grasp server"))
|
||||||
|
.on_click({
|
||||||
|
let input = input.clone();
|
||||||
|
move |_, window, cx| add_server(&input, window, cx)
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display_server(server: &str) -> SharedString {
|
||||||
|
match normalize_server(server) {
|
||||||
|
Some((_, relay)) => server_host(&relay),
|
||||||
|
None => SharedString::from(server.to_owned()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_server(input: &Entity<InputState>, window: &mut Window, cx: &mut App) {
|
||||||
|
let value = input.read(cx).value().trim().to_owned();
|
||||||
|
if value.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let Some((normalized, _)) = normalize_server(&value) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
SettingsStore::global(cx).update(cx, |store, cx| {
|
||||||
|
store.edit(
|
||||||
|
|settings| {
|
||||||
|
if !settings.grasp_servers.default_servers.contains(&normalized) {
|
||||||
|
settings
|
||||||
|
.grasp_servers
|
||||||
|
.default_servers
|
||||||
|
.push(normalized.clone());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
input.update(cx, |input, cx| input.set_value("", window, cx));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scan_paths_editor(cx: &App) -> AnyElement {
|
||||||
|
let scan_paths = SettingsStore::global(cx)
|
||||||
|
.read(cx)
|
||||||
|
.settings()
|
||||||
|
.local_repos
|
||||||
|
.scan_paths
|
||||||
|
.clone();
|
||||||
|
let store = SettingsStore::global(cx);
|
||||||
|
|
||||||
|
v_flex()
|
||||||
|
.gap_2()
|
||||||
|
.children(scan_paths.iter().enumerate().map(|(ix, path)| {
|
||||||
|
let store = store.clone();
|
||||||
|
h_flex()
|
||||||
|
.gap_1()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex_1()
|
||||||
|
.h_8()
|
||||||
|
.px_2()
|
||||||
|
.bg(cx.theme().colors.muted)
|
||||||
|
.text_color(cx.theme().colors.muted_foreground)
|
||||||
|
.text_sm()
|
||||||
|
.rounded(cx.theme().radius.control)
|
||||||
|
.child(path.display().to_string()),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new(format!("settings-remove-path:{ix}"))
|
||||||
|
.icon(Icon::named(IconRole::Close))
|
||||||
|
.variant(gpui_native_kit::button::ButtonVariant::Ghost)
|
||||||
|
.tooltip(Tooltip::text("Remove"))
|
||||||
|
.on_click(move |_, _, cx| {
|
||||||
|
store.update(cx, |store, cx| {
|
||||||
|
store.edit(
|
||||||
|
|settings| {
|
||||||
|
settings.local_repos.scan_paths.remove(ix);
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
}))
|
||||||
|
.child(
|
||||||
|
h_flex().justify_end().child(
|
||||||
|
Button::new("settings-add-path")
|
||||||
|
.icon(Icon::named(IconRole::Plus))
|
||||||
|
.label("Add directory")
|
||||||
|
.on_click(|_, _, cx| add_scan_path(cx)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_scan_path(cx: &mut App) {
|
||||||
|
let prompt = cx.prompt_for_paths(PathPromptOptions {
|
||||||
|
files: false,
|
||||||
|
directories: true,
|
||||||
|
multiple: true,
|
||||||
|
prompt: Some("Choose directories".into()),
|
||||||
|
});
|
||||||
|
cx.spawn(async move |cx| {
|
||||||
|
if let Ok(Ok(Some(paths))) = prompt.await {
|
||||||
|
cx.update(|cx| {
|
||||||
|
let store = SettingsStore::global(cx);
|
||||||
|
store.update(cx, |store, cx| {
|
||||||
|
store.edit(
|
||||||
|
|settings| {
|
||||||
|
for path in paths {
|
||||||
|
let path = path.to_string_lossy().to_string();
|
||||||
|
if !settings
|
||||||
|
.local_repos
|
||||||
|
.scan_paths
|
||||||
|
.iter()
|
||||||
|
.any(|existing| existing.to_string_lossy() == path)
|
||||||
|
{
|
||||||
|
settings.local_repos.scan_paths.push(PathBuf::from(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn default_folder_selector(
|
||||||
|
default_folder: &Entity<InputState>,
|
||||||
|
weak: WeakEntity<SettingsDialog>,
|
||||||
|
) -> AnyElement {
|
||||||
|
let folder = default_folder.clone();
|
||||||
|
h_flex()
|
||||||
|
.gap_1()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex_1()
|
||||||
|
.child(Input::new("settings-default-folder", folder.clone())),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new("settings-choose-folder")
|
||||||
|
.icon(Icon::named(IconRole::Folder))
|
||||||
|
.variant(gpui_native_kit::button::ButtonVariant::Ghost)
|
||||||
|
.tooltip(Tooltip::text("Choose folder"))
|
||||||
|
.on_click(move |_, window, cx| {
|
||||||
|
choose_default_folder(&folder, &weak, window, cx);
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn choose_default_folder(
|
||||||
|
default_folder: &Entity<InputState>,
|
||||||
|
weak: &WeakEntity<SettingsDialog>,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut App,
|
||||||
|
) {
|
||||||
|
let handle = window.window_handle();
|
||||||
|
let default_folder = default_folder.clone();
|
||||||
|
let weak = weak.clone();
|
||||||
|
let prompt = cx.prompt_for_paths(PathPromptOptions {
|
||||||
|
files: false,
|
||||||
|
directories: true,
|
||||||
|
multiple: false,
|
||||||
|
prompt: Some("Choose default folder".into()),
|
||||||
|
});
|
||||||
|
cx.spawn(async move |cx| {
|
||||||
|
if let Ok(Ok(Some(mut paths))) = prompt.await
|
||||||
|
&& let Some(path) = paths.pop()
|
||||||
|
{
|
||||||
|
let text = path.to_string_lossy().to_string();
|
||||||
|
if let Err(error) = cx.update_window(handle, |_, window, cx| {
|
||||||
|
let store = SettingsStore::global(cx);
|
||||||
|
store.update(cx, |store, cx| {
|
||||||
|
store.edit(
|
||||||
|
|settings| settings.create_repository.default_folder = Some(path.clone()),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
default_folder.update(cx, |input, cx| input.set_value(text, window, cx));
|
||||||
|
let _ = weak.update(cx, |_this, cx| cx.notify());
|
||||||
|
}) {
|
||||||
|
log::warn!("failed to apply default folder: {error}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
mod helpers;
|
||||||
|
|
||||||
|
use gpui::{
|
||||||
|
App, AppContext as _, Context, Entity, IntoElement, ParentElement as _, Render, Styled as _,
|
||||||
|
Window, px,
|
||||||
|
};
|
||||||
|
use gpui_native_kit::common::v_flex;
|
||||||
|
use gpui_native_kit::dialog::Dialog;
|
||||||
|
use gpui_native_kit::input::InputState;
|
||||||
|
use gpui_native_kit::scroll_area::ScrollArea;
|
||||||
|
use gpui_native_kit::separator::Separator;
|
||||||
|
use helpers::{
|
||||||
|
appearance_select, default_folder_selector, event_fetching_select, grasp_servers_editor,
|
||||||
|
scan_paths_editor, setting_block, setting_row,
|
||||||
|
};
|
||||||
|
use settings::SettingsStore;
|
||||||
|
|
||||||
|
use crate::shared::dialogs::open_global;
|
||||||
|
|
||||||
|
pub struct SettingsDialog {
|
||||||
|
grasp_input: Entity<InputState>,
|
||||||
|
default_folder: Entity<InputState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SettingsDialog {
|
||||||
|
pub fn new(cx: &mut App) -> Entity<Self> {
|
||||||
|
let store = SettingsStore::global(cx);
|
||||||
|
let settings = store.read(cx).settings().clone();
|
||||||
|
let grasp_input = InputState::new(cx);
|
||||||
|
|
||||||
|
let folder = settings
|
||||||
|
.create_repository
|
||||||
|
.default_folder
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(paths::desktop_dir);
|
||||||
|
|
||||||
|
let default_folder = InputState::builder()
|
||||||
|
.text(folder.to_string_lossy())
|
||||||
|
.read_only()
|
||||||
|
.build(cx);
|
||||||
|
|
||||||
|
cx.new(|_| Self {
|
||||||
|
grasp_input,
|
||||||
|
default_folder,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for SettingsDialog {
|
||||||
|
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
|
let grasp_input = self.grasp_input.clone();
|
||||||
|
let default_folder = self.default_folder.clone();
|
||||||
|
let weak = cx.entity().downgrade();
|
||||||
|
|
||||||
|
Dialog::new("settings-dialog")
|
||||||
|
.open(true)
|
||||||
|
.min_width(px(650.))
|
||||||
|
.on_change(|open, _, cx| {
|
||||||
|
if !open {
|
||||||
|
crate::shared::dialogs::close_global(cx);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.title("Settings")
|
||||||
|
.content(move |_, cx| {
|
||||||
|
ScrollArea::new("settings-scroll")
|
||||||
|
.max_h(px(560.))
|
||||||
|
.child(
|
||||||
|
v_flex()
|
||||||
|
.gap_4()
|
||||||
|
.child(setting_row(
|
||||||
|
cx,
|
||||||
|
"Appearance",
|
||||||
|
"Choose whether the app follows the system theme or uses a light/dark theme.",
|
||||||
|
appearance_select(cx),
|
||||||
|
))
|
||||||
|
.child(Separator::horizontal())
|
||||||
|
.child(setting_row(
|
||||||
|
cx,
|
||||||
|
"Event Fetching Strategy",
|
||||||
|
"Curated fetches events from relays in the repository's announcement. Uncensored also fetches from every maintainer's relays.",
|
||||||
|
event_fetching_select(cx),
|
||||||
|
))
|
||||||
|
.child(Separator::horizontal())
|
||||||
|
.child(setting_block(
|
||||||
|
cx,
|
||||||
|
"Grasp Servers",
|
||||||
|
"Servers used to host your git repositories via the Grasp protocol",
|
||||||
|
grasp_servers_editor(&grasp_input, cx),
|
||||||
|
))
|
||||||
|
.child(Separator::horizontal())
|
||||||
|
.child(setting_block(
|
||||||
|
cx,
|
||||||
|
"Scan Directories",
|
||||||
|
"Directories scanned for local git repositories.",
|
||||||
|
scan_paths_editor(cx),
|
||||||
|
))
|
||||||
|
.child(setting_block(
|
||||||
|
cx,
|
||||||
|
"Default Folder",
|
||||||
|
"The folder for newly created repositories.",
|
||||||
|
default_folder_selector(&default_folder, weak.clone()),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Opens the settings dialog.
|
||||||
|
pub fn open(cx: &mut App) {
|
||||||
|
open_global(SettingsDialog::new(cx), cx);
|
||||||
|
}
|
||||||
@@ -30,6 +30,7 @@ use crate::explore::ExploreView;
|
|||||||
use crate::identity;
|
use crate::identity;
|
||||||
use crate::inbox::InboxView;
|
use crate::inbox::InboxView;
|
||||||
use crate::repo::dialogs;
|
use crate::repo::dialogs;
|
||||||
|
use crate::settings;
|
||||||
|
|
||||||
mod helpers;
|
mod helpers;
|
||||||
|
|
||||||
@@ -226,6 +227,7 @@ impl SidebarView {
|
|||||||
Sidebar::new("signed-sidebar")
|
Sidebar::new("signed-sidebar")
|
||||||
.transparent(true)
|
.transparent(true)
|
||||||
.header(self.render_user(&profile, self.scanning, cx))
|
.header(self.render_user(&profile, self.scanning, cx))
|
||||||
|
.footer(self.render_footer(cx))
|
||||||
.entries(move |_, cx| {
|
.entries(move |_, cx| {
|
||||||
let muted = cx.theme().colors.muted_foreground;
|
let muted = cx.theme().colors.muted_foreground;
|
||||||
let mut entries = vec![
|
let mut entries = vec![
|
||||||
@@ -439,6 +441,34 @@ impl SidebarView {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_footer(&self, _cx: &App) -> impl IntoElement {
|
||||||
|
let guide_weak = self.weak.clone();
|
||||||
|
let settings_click = |_: &gpui::ClickEvent, _: &mut Window, cx: &mut gpui::App| {
|
||||||
|
settings::open(cx);
|
||||||
|
};
|
||||||
|
|
||||||
|
v_flex()
|
||||||
|
.p_2()
|
||||||
|
.gap_1()
|
||||||
|
.w_full()
|
||||||
|
.child(
|
||||||
|
Button::new("guide")
|
||||||
|
.label("Guide")
|
||||||
|
.variant(ButtonVariant::Ghost)
|
||||||
|
.on_click(move |_, window, cx| {
|
||||||
|
let _ = guide_weak.update(cx, |this, cx| {
|
||||||
|
this.open_explore(window, cx);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new("settings")
|
||||||
|
.label("Settings")
|
||||||
|
.variant(ButtonVariant::Ghost)
|
||||||
|
.on_click(settings_click),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn open_inbox(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
fn open_inbox(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
let dock = self.dock.clone();
|
let dock = self.dock.clone();
|
||||||
let dock_for_build = dock.clone();
|
let dock_for_build = dock.clone();
|
||||||
|
|||||||
Reference in New Issue
Block a user