settings_ui: Add placeholder view (#15019)

Marshall Bowers created

This PR adds a placeholder view for the settings UI. It does not contain
any functionality, as of yet.

This view is staff-shipped behind a feature flag.

Release Notes:

- N/A

Change summary

Cargo.lock                            |  12 +++
Cargo.toml                            |   4 
crates/settings_ui/Cargo.toml         |  19 +++++
crates/settings_ui/LICENSE-GPL        |   1 
crates/settings_ui/src/settings_ui.rs | 107 +++++++++++++++++++++++++++++
crates/zed/Cargo.toml                 |   1 
crates/zed/src/main.rs                |   1 
7 files changed, 144 insertions(+), 1 deletion(-)

Detailed changes

Cargo.lock 🔗

@@ -9738,6 +9738,17 @@ dependencies = [
  "util",
 ]
 
+[[package]]
+name = "settings_ui"
+version = "0.1.0"
+dependencies = [
+ "command_palette_hooks",
+ "feature_flags",
+ "gpui",
+ "ui",
+ "workspace",
+]
+
 [[package]]
 name = "sha-1"
 version = "0.9.8"
@@ -13809,6 +13820,7 @@ dependencies = [
  "serde",
  "serde_json",
  "settings",
+ "settings_ui",
  "simplelog",
  "smol",
  "snippet_provider",

Cargo.toml 🔗

@@ -91,6 +91,7 @@ members = [
     "crates/semantic_index",
     "crates/semantic_version",
     "crates/settings",
+    "crates/settings_ui",
     "crates/snippet",
     "crates/snippet_provider",
     "crates/sqlez",
@@ -165,7 +166,6 @@ assets = { path = "crates/assets" }
 assistant = { path = "crates/assistant" }
 assistant_slash_command = { path = "crates/assistant_slash_command" }
 assistant_tooling = { path = "crates/assistant_tooling" }
-async-pipe = { git = "https://github.com/zed-industries/async-pipe-rs", rev = "82d00a04211cf4e1236029aa03e6b6ce2a74c553" }
 audio = { path = "crates/audio" }
 auto_update = { path = "crates/auto_update" }
 breadcrumbs = { path = "crates/breadcrumbs" }
@@ -249,6 +249,7 @@ search = { path = "crates/search" }
 semantic_index = { path = "crates/semantic_index" }
 semantic_version = { path = "crates/semantic_version" }
 settings = { path = "crates/settings" }
+settings_ui = { path = "crates/settings_ui" }
 snippet = { path = "crates/snippet" }
 snippet_provider = { path = "crates/snippet_provider" }
 sqlez = { path = "crates/sqlez" }
@@ -288,6 +289,7 @@ ashpd = "0.9.1"
 async-compression = { version = "0.4", features = ["gzip", "futures-io"] }
 async-dispatcher = { version = "0.1" }
 async-fs = "1.6"
+async-pipe = { git = "https://github.com/zed-industries/async-pipe-rs", rev = "82d00a04211cf4e1236029aa03e6b6ce2a74c553" }
 async-recursion = "1.0.0"
 async-tar = "0.4.2"
 async-trait = "0.1"

crates/settings_ui/Cargo.toml 🔗

@@ -0,0 +1,19 @@
+[package]
+name = "settings_ui"
+version = "0.1.0"
+edition = "2021"
+publish = false
+license = "GPL-3.0-or-later"
+
+[lints]
+workspace = true
+
+[lib]
+path = "src/settings_ui.rs"
+
+[dependencies]
+command_palette_hooks.workspace = true
+feature_flags.workspace = true
+gpui.workspace = true
+ui.workspace = true
+workspace.workspace = true

crates/settings_ui/src/settings_ui.rs 🔗

@@ -0,0 +1,107 @@
+use std::any::TypeId;
+
+use command_palette_hooks::CommandPaletteFilter;
+use feature_flags::{FeatureFlag, FeatureFlagViewExt};
+use gpui::{actions, AppContext, EventEmitter, FocusHandle, FocusableView, View};
+use ui::prelude::*;
+use workspace::item::{Item, ItemEvent};
+use workspace::Workspace;
+
+pub struct SettingsUiFeatureFlag;
+
+impl FeatureFlag for SettingsUiFeatureFlag {
+    const NAME: &'static str = "settings-ui";
+}
+
+actions!(zed, [OpenSettingsEditor]);
+
+pub fn init(cx: &mut AppContext) {
+    cx.observe_new_views(|workspace: &mut Workspace, cx| {
+        workspace.register_action(|workspace, _: &OpenSettingsEditor, cx| {
+            let existing = workspace
+                .active_pane()
+                .read(cx)
+                .items()
+                .find_map(|item| item.downcast::<SettingsPage>());
+
+            if let Some(existing) = existing {
+                workspace.activate_item(&existing, true, true, cx);
+            } else {
+                let settings_page = SettingsPage::new(workspace, cx);
+                workspace.add_item_to_active_pane(Box::new(settings_page), None, true, cx)
+            }
+        });
+
+        let settings_ui_actions = [TypeId::of::<OpenSettingsEditor>()];
+
+        CommandPaletteFilter::update_global(cx, |filter, _cx| {
+            filter.hide_action_types(&settings_ui_actions);
+        });
+
+        cx.observe_flag::<SettingsUiFeatureFlag, _>(move |is_enabled, _view, cx| {
+            if is_enabled {
+                CommandPaletteFilter::update_global(cx, |filter, _cx| {
+                    filter.show_action_types(settings_ui_actions.iter());
+                });
+            } else {
+                CommandPaletteFilter::update_global(cx, |filter, _cx| {
+                    filter.hide_action_types(&settings_ui_actions);
+                });
+            }
+        })
+        .detach();
+    })
+    .detach();
+}
+
+pub struct SettingsPage {
+    focus_handle: FocusHandle,
+}
+
+impl SettingsPage {
+    pub fn new(_workspace: &Workspace, cx: &mut ViewContext<Workspace>) -> View<Self> {
+        cx.new_view(|cx| Self {
+            focus_handle: cx.focus_handle(),
+        })
+    }
+}
+
+impl EventEmitter<ItemEvent> for SettingsPage {}
+
+impl FocusableView for SettingsPage {
+    fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
+        self.focus_handle.clone()
+    }
+}
+
+impl Item for SettingsPage {
+    type Event = ItemEvent;
+
+    fn tab_icon(&self, _cx: &WindowContext) -> Option<Icon> {
+        Some(Icon::new(IconName::Settings))
+    }
+
+    fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
+        Some("Settings".into())
+    }
+
+    fn show_toolbar(&self) -> bool {
+        false
+    }
+
+    fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {
+        f(*event)
+    }
+}
+
+impl Render for SettingsPage {
+    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
+        v_flex()
+            .p_4()
+            .size_full()
+            .child(Label::new("Settings").size(LabelSize::Large))
+            .child(Label::new(
+                "Nothing to see here yet. Feature-flagged for staff.",
+            ))
+    }
+}

crates/zed/Cargo.toml 🔗

@@ -86,6 +86,7 @@ search.workspace = true
 serde.workspace = true
 serde_json.workspace = true
 settings.workspace = true
+settings_ui.workspace = true
 simplelog.workspace = true
 smol.workspace = true
 snippet_provider.workspace = true

crates/zed/src/main.rs 🔗

@@ -229,6 +229,7 @@ fn init_ui(app_state: Arc<AppState>, cx: &mut AppContext) -> Result<()> {
     feedback::init(cx);
     markdown_preview::init(cx);
     welcome::init(cx);
+    settings_ui::init(cx);
     extensions_ui::init(cx);
 
     // Initialize each completion provider. Settings are used for toggling between them.