Revert "Remove cx from ThemeSettings (#38836)" (#39691)

Conrad Irwin created

This reverts commit a2a7bd139a3f94202d2a050a2bc7e50dc139000b.

This caused themes to not load correctly on startup, you needed to edit
your settings.

Release Notes:

- N/A

Change summary

Cargo.lock                                                        |   1 
crates/agent/src/thread.rs                                        |   3 
crates/agent_settings/src/agent_settings.rs                       |   2 
crates/agent_ui/src/acp/entry_view_state.rs                       |   3 
crates/agent_ui/src/acp/thread_view.rs                            |   2 
crates/agent_ui/src/agent_diff.rs                                 |   5 
crates/assistant_tools/src/terminal_tool.rs                       |   3 
crates/audio/src/audio_settings.rs                                |   2 
crates/auto_update/src/auto_update.rs                             |   2 
crates/call/src/call_settings.rs                                  |   9 
crates/client/src/client.rs                                       |   6 
crates/collab/src/tests/editor_tests.rs                           |   4 
crates/collab/src/tests/randomized_test_helpers.rs                |   5 
crates/collab/src/tests/test_server.rs                            |   1 
crates/collab_ui/src/panel_settings.rs                            |   4 
crates/dap/src/debugger_settings.rs                               |   3 
crates/editor/src/editor_settings.rs                              |   2 
crates/extension_host/src/extension_settings.rs                   |   3 
crates/file_finder/src/file_finder_settings.rs                    |   2 
crates/file_icons/Cargo.toml                                      |   1 
crates/file_icons/src/file_icons.rs                               |  45 
crates/git_hosting_providers/src/settings.rs                      |   2 
crates/git_ui/src/file_diff_view.rs                               |   4 
crates/git_ui/src/git_panel_settings.rs                           |   2 
crates/git_ui/src/text_diff_view.rs                               |   4 
crates/go_to_line/src/cursor_position.rs                          |   2 
crates/image_viewer/src/image_viewer_settings.rs                  |   3 
crates/journal/src/journal.rs                                     |   2 
crates/language/src/language_settings.rs                          |   2 
crates/language_models/src/settings.rs                            |   2 
crates/onboarding/src/basics_page.rs                              |  12 
crates/outline_panel/src/outline_panel_settings.rs                |   2 
crates/project/src/agent_server_store.rs                          |   8 
crates/project/src/project.rs                                     |   2 
crates/project/src/project_settings.rs                            |   4 
crates/project_panel/src/project_panel_settings.rs                |   2 
crates/recent_projects/src/remote_connections.rs                  |   2 
crates/repl/src/jupyter_settings.rs                               |   2 
crates/repl/src/repl_settings.rs                                  |   3 
crates/settings/src/base_keymap_setting.rs                        |   3 
crates/settings/src/settings_store.rs                             |  55 
crates/settings_profile_selector/src/settings_profile_selector.rs |   1 
crates/storybook/src/storybook.rs                                 |   6 
crates/terminal/src/terminal_settings.rs                          |   4 
crates/theme/src/fallback_themes.rs                               |   8 
crates/theme/src/settings.rs                                      | 210 
crates/theme/src/theme.rs                                         | 137 
crates/theme_extension/src/theme_extension.rs                     |   6 
crates/theme_selector/src/icon_theme_selector.rs                  |  51 
crates/theme_selector/src/theme_selector.rs                       |   7 
crates/title_bar/src/title_bar_settings.rs                        |   3 
crates/vim/src/vim.rs                                             |   2 
crates/vim_mode_setting/src/vim_mode_setting.rs                   |   4 
crates/workspace/src/item.rs                                      |   4 
crates/workspace/src/workspace.rs                                 |   6 
crates/workspace/src/workspace_settings.rs                        |   7 
crates/worktree/src/worktree_settings.rs                          |   3 
crates/zed/src/main.rs                                            |  87 
crates/zed/src/zed.rs                                             |   9 
crates/zlog_settings/src/zlog_settings.rs                         |   2 
60 files changed, 423 insertions(+), 360 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -5921,6 +5921,7 @@ version = "0.1.0"
 dependencies = [
  "gpui",
  "serde",
+ "settings",
  "theme",
  "workspace-hack",
  "zed-util",

crates/agent/src/thread.rs 🔗

@@ -3220,6 +3220,7 @@ mod tests {
     use settings::{LanguageModelParameters, Settings, SettingsStore};
     use std::sync::Arc;
     use std::time::Duration;
+    use theme::ThemeSettings;
     use util::path;
     use workspace::Workspace;
 
@@ -5280,7 +5281,7 @@ fn main() {{
             thread_store::init(fs.clone(), cx);
             workspace::init_settings(cx);
             language_model::init_settings(cx);
-            theme::init(theme::LoadThemes::JustBase, cx);
+            ThemeSettings::register(cx);
             ToolRegistry::default_global(cx);
             assistant_tool::init(cx);
 

crates/agent_settings/src/agent_settings.rs 🔗

@@ -151,7 +151,7 @@ impl Default for AgentProfileId {
 }
 
 impl Settings for AgentSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         let agent = content.agent.clone().unwrap();
         Self {
             enabled: agent.enabled.unwrap(),

crates/agent_ui/src/acp/entry_view_state.rs 🔗

@@ -414,6 +414,7 @@ mod tests {
     use project::Project;
     use serde_json::json;
     use settings::{Settings as _, SettingsStore};
+    use theme::ThemeSettings;
     use util::path;
     use workspace::Workspace;
 
@@ -543,7 +544,7 @@ mod tests {
             Project::init_settings(cx);
             AgentSettings::register(cx);
             workspace::init_settings(cx);
-            theme::init(theme::LoadThemes::JustBase, cx);
+            ThemeSettings::register(cx);
             release_channel::init(SemanticVersion::default(), cx);
             EditorSettings::register(cx);
         });

crates/agent_ui/src/acp/thread_view.rs 🔗

@@ -6086,7 +6086,7 @@ pub(crate) mod tests {
             Project::init_settings(cx);
             AgentSettings::register(cx);
             workspace::init_settings(cx);
-            theme::init(theme::LoadThemes::JustBase, cx);
+            ThemeSettings::register(cx);
             release_channel::init(SemanticVersion::default(), cx);
             EditorSettings::register(cx);
             prompt_store::init(cx)

crates/agent_ui/src/agent_diff.rs 🔗

@@ -1814,6 +1814,7 @@ mod tests {
     use serde_json::json;
     use settings::{Settings, SettingsStore};
     use std::{path::Path, rc::Rc};
+    use theme::ThemeSettings;
     use util::path;
 
     #[gpui::test]
@@ -1826,7 +1827,7 @@ mod tests {
             AgentSettings::register(cx);
             prompt_store::init(cx);
             workspace::init_settings(cx);
-            theme::init(theme::LoadThemes::JustBase, cx);
+            ThemeSettings::register(cx);
             EditorSettings::register(cx);
             language_model::init_settings(cx);
         });
@@ -1978,7 +1979,7 @@ mod tests {
             AgentSettings::register(cx);
             prompt_store::init(cx);
             workspace::init_settings(cx);
-            theme::init(theme::LoadThemes::JustBase, cx);
+            ThemeSettings::register(cx);
             EditorSettings::register(cx);
             language_model::init_settings(cx);
             workspace::register_project_item::<Editor>(cx);

crates/assistant_tools/src/terminal_tool.rs 🔗

@@ -704,6 +704,7 @@ mod tests {
     use serde_json::json;
     use settings::{Settings, SettingsStore};
     use terminal::terminal_settings::TerminalSettings;
+    use theme::ThemeSettings;
     use util::{ResultExt as _, test::TempTree};
 
     use super::*;
@@ -718,7 +719,7 @@ mod tests {
             language::init(cx);
             Project::init_settings(cx);
             workspace::init_settings(cx);
-            theme::init(theme::LoadThemes::JustBase, cx);
+            ThemeSettings::register(cx);
             TerminalSettings::register(cx);
             EditorSettings::register(cx);
         });

crates/audio/src/audio_settings.rs 🔗

@@ -42,7 +42,7 @@ pub struct AudioSettings {
 
 /// Configuration of audio in Zed
 impl Settings for AudioSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         let audio = &content.audio.as_ref().unwrap();
         AudioSettings {
             rodio_audio: audio.rodio_audio.unwrap(),

crates/auto_update/src/auto_update.rs 🔗

@@ -127,7 +127,7 @@ struct AutoUpdateSetting(bool);
 ///
 /// Default: true
 impl Settings for AutoUpdateSetting {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         Self(content.auto_update.unwrap())
     }
 }

crates/call/src/call_settings.rs 🔗

@@ -1,3 +1,4 @@
+use gpui::App;
 use settings::Settings;
 
 #[derive(Debug)]
@@ -7,11 +8,17 @@ pub struct CallSettings {
 }
 
 impl Settings for CallSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         let call = content.calls.clone().unwrap();
         CallSettings {
             mute_on_join: call.mute_on_join.unwrap(),
             share_on_join: call.share_on_join.unwrap(),
         }
     }
+
+    fn import_from_vscode(
+        _vscode: &settings::VsCodeSettings,
+        _current: &mut settings::SettingsContent,
+    ) {
+    }
 }

crates/client/src/client.rs 🔗

@@ -101,7 +101,7 @@ pub struct ClientSettings {
 }
 
 impl Settings for ClientSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         if let Some(server_url) = &*ZED_SERVER_URL {
             return Self {
                 server_url: server_url.clone(),
@@ -133,7 +133,7 @@ impl ProxySettings {
 }
 
 impl Settings for ProxySettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         Self {
             proxy: content.proxy.clone(),
         }
@@ -519,7 +519,7 @@ pub struct TelemetrySettings {
 }
 
 impl settings::Settings for TelemetrySettings {
-    fn from_settings(content: &SettingsContent) -> Self {
+    fn from_settings(content: &SettingsContent, _cx: &mut App) -> Self {
         Self {
             diagnostics: content.telemetry.as_ref().unwrap().diagnostics.unwrap(),
             metrics: content.telemetry.as_ref().unwrap().metrics.unwrap(),

crates/collab/src/tests/editor_tests.rs 🔗

@@ -2041,10 +2041,6 @@ async fn test_mutual_editor_inlay_hint_cache_update(
     });
 }
 
-// This test started hanging on seed 2 after the theme settings
-// PR. The hypothesis is that it's been buggy for a while, but got lucky
-// on seeds.
-#[ignore]
 #[gpui::test(iterations = 10)]
 async fn test_inlay_hint_refresh_is_forwarded(
     cx_a: &mut TestAppContext,

crates/collab/src/tests/randomized_test_helpers.rs 🔗

@@ -183,10 +183,9 @@ pub async fn run_randomized_test<T: RandomizedTest>(
 
     for (client, cx) in clients {
         cx.update(|cx| {
-            let settings = cx.remove_global::<SettingsStore>();
+            let store = cx.remove_global::<SettingsStore>();
             cx.clear_globals();
-            cx.set_global(settings);
-            theme::init(theme::LoadThemes::JustBase, cx);
+            cx.set_global(store);
             drop(client);
         });
     }

crates/collab/src/tests/test_server.rs 🔗

@@ -172,7 +172,6 @@ impl TestServer {
             }
             let settings = SettingsStore::test(cx);
             cx.set_global(settings);
-            theme::init(theme::LoadThemes::JustBase, cx);
             release_channel::init(SemanticVersion::default(), cx);
             client::init_settings(cx);
         });

crates/collab_ui/src/panel_settings.rs 🔗

@@ -18,7 +18,7 @@ pub struct NotificationPanelSettings {
 }
 
 impl Settings for CollaborationPanelSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut ui::App) -> Self {
         let panel = content.collaboration_panel.as_ref().unwrap();
 
         Self {
@@ -30,7 +30,7 @@ impl Settings for CollaborationPanelSettings {
 }
 
 impl Settings for NotificationPanelSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut ui::App) -> Self {
         let panel = content.notification_panel.as_ref().unwrap();
         return Self {
             button: panel.button.unwrap(),

crates/dap/src/debugger_settings.rs 🔗

@@ -1,4 +1,5 @@
 use dap_types::SteppingGranularity;
+use gpui::App;
 use settings::{Settings, SettingsContent};
 
 pub struct DebuggerSettings {
@@ -33,7 +34,7 @@ pub struct DebuggerSettings {
 }
 
 impl Settings for DebuggerSettings {
-    fn from_settings(content: &SettingsContent) -> Self {
+    fn from_settings(content: &SettingsContent, _cx: &mut App) -> Self {
         let content = content.debugger.clone().unwrap();
         Self {
             stepping_granularity: dap_granularity_from_settings(

crates/editor/src/editor_settings.rs 🔗

@@ -176,7 +176,7 @@ impl ScrollbarVisibility for EditorSettings {
 }
 
 impl Settings for EditorSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         let editor = content.editor.clone();
         let scrollbar = editor.scrollbar.unwrap();
         let minimap = editor.minimap.unwrap();

crates/extension_host/src/extension_settings.rs 🔗

@@ -2,6 +2,7 @@ use collections::HashMap;
 use extension::{
     DownloadFileCapability, ExtensionCapability, NpmInstallPackageCapability, ProcessExecCapability,
 };
+use gpui::App;
 use settings::Settings;
 use std::sync::Arc;
 
@@ -36,7 +37,7 @@ impl ExtensionSettings {
 }
 
 impl Settings for ExtensionSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         Self {
             auto_install_extensions: content.extension.auto_install_extensions.clone(),
             auto_update_extensions: content.extension.auto_update_extensions.clone(),

crates/file_finder/src/file_finder_settings.rs 🔗

@@ -11,7 +11,7 @@ pub struct FileFinderSettings {
 }
 
 impl Settings for FileFinderSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut ui::App) -> Self {
         let file_finder = content.file_finder.as_ref().unwrap();
 
         Self {

crates/file_icons/Cargo.toml 🔗

@@ -15,6 +15,7 @@ doctest = false
 [dependencies]
 gpui.workspace = true
 serde.workspace = true
+settings.workspace = true
 theme.workspace = true
 util.workspace = true
 workspace-hack.workspace = true

crates/file_icons/src/file_icons.rs 🔗

@@ -2,7 +2,8 @@ use std::sync::Arc;
 use std::{path::Path, str};
 
 use gpui::{App, SharedString};
-use theme::{GlobalTheme, IconTheme, ThemeRegistry};
+use settings::Settings;
+use theme::{IconTheme, ThemeRegistry, ThemeSettings};
 use util::paths::PathExt;
 
 #[derive(Debug)]
@@ -12,8 +13,10 @@ pub struct FileIcons {
 
 impl FileIcons {
     pub fn get(cx: &App) -> Self {
+        let theme_settings = ThemeSettings::get_global(cx);
+
         Self {
-            icon_theme: GlobalTheme::icon_theme(cx).clone(),
+            icon_theme: theme_settings.active_icon_theme.clone(),
         }
     }
 
@@ -94,7 +97,7 @@ impl FileIcons {
                 .map(|icon_definition| icon_definition.path.clone())
         }
 
-        get_icon_for_type(GlobalTheme::icon_theme(cx), typ).or_else(|| {
+        get_icon_for_type(&ThemeSettings::get_global(cx).active_icon_theme, typ).or_else(|| {
             Self::default_icon_theme(cx).and_then(|icon_theme| get_icon_for_type(&icon_theme, typ))
         })
     }
@@ -119,16 +122,20 @@ impl FileIcons {
             }
         }
 
-        get_folder_icon(GlobalTheme::icon_theme(cx), path, expanded)
-            .or_else(|| {
-                Self::default_icon_theme(cx)
-                    .and_then(|icon_theme| get_folder_icon(&icon_theme, path, expanded))
-            })
-            .or_else(|| {
-                // If we can't find a specific folder icon for the folder at the given path, fall back to the generic folder
-                // icon.
-                Self::get_generic_folder_icon(expanded, cx)
-            })
+        get_folder_icon(
+            &ThemeSettings::get_global(cx).active_icon_theme,
+            path,
+            expanded,
+        )
+        .or_else(|| {
+            Self::default_icon_theme(cx)
+                .and_then(|icon_theme| get_folder_icon(&icon_theme, path, expanded))
+        })
+        .or_else(|| {
+            // If we can't find a specific folder icon for the folder at the given path, fall back to the generic folder
+            // icon.
+            Self::get_generic_folder_icon(expanded, cx)
+        })
     }
 
     fn get_generic_folder_icon(expanded: bool, cx: &App) -> Option<SharedString> {
@@ -143,10 +150,12 @@ impl FileIcons {
             }
         }
 
-        get_generic_folder_icon(GlobalTheme::icon_theme(cx), expanded).or_else(|| {
-            Self::default_icon_theme(cx)
-                .and_then(|icon_theme| get_generic_folder_icon(&icon_theme, expanded))
-        })
+        get_generic_folder_icon(&ThemeSettings::get_global(cx).active_icon_theme, expanded).or_else(
+            || {
+                Self::default_icon_theme(cx)
+                    .and_then(|icon_theme| get_generic_folder_icon(&icon_theme, expanded))
+            },
+        )
     }
 
     pub fn get_chevron_icon(expanded: bool, cx: &App) -> Option<SharedString> {
@@ -158,7 +167,7 @@ impl FileIcons {
             }
         }
 
-        get_chevron_icon(GlobalTheme::icon_theme(cx), expanded).or_else(|| {
+        get_chevron_icon(&ThemeSettings::get_global(cx).active_icon_theme, expanded).or_else(|| {
             Self::default_icon_theme(cx)
                 .and_then(|icon_theme| get_chevron_icon(&icon_theme, expanded))
         })

crates/git_hosting_providers/src/settings.rs 🔗

@@ -58,7 +58,7 @@ pub struct GitHostingProviderSettings {
 }
 
 impl Settings for GitHostingProviderSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         Self {
             git_hosting_providers: content
                 .project

crates/git_ui/src/file_diff_view.rs 🔗

@@ -360,7 +360,7 @@ mod tests {
     use editor::test::editor_test_context::assert_state_with_diff;
     use gpui::TestAppContext;
     use project::{FakeFs, Fs, Project};
-    use settings::SettingsStore;
+    use settings::{Settings, SettingsStore};
     use std::path::PathBuf;
     use unindent::unindent;
     use util::path;
@@ -374,7 +374,7 @@ mod tests {
             Project::init_settings(cx);
             workspace::init_settings(cx);
             editor::init_settings(cx);
-            theme::init(theme::LoadThemes::JustBase, cx);
+            theme::ThemeSettings::register(cx)
         });
     }
 

crates/git_ui/src/git_panel_settings.rs 🔗

@@ -43,7 +43,7 @@ impl ScrollbarVisibility for GitPanelSettings {
 }
 
 impl Settings for GitPanelSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut ui::App) -> Self {
         let git_panel = content.git_panel.clone().unwrap();
         Self {
             button: git_panel.button.unwrap(),

crates/git_ui/src/text_diff_view.rs 🔗

@@ -450,7 +450,7 @@ mod tests {
     use gpui::{TestAppContext, VisualContext};
     use project::{FakeFs, Project};
     use serde_json::json;
-    use settings::SettingsStore;
+    use settings::{Settings, SettingsStore};
     use unindent::unindent;
     use util::{path, test::marked_text_ranges};
 
@@ -462,7 +462,7 @@ mod tests {
             Project::init_settings(cx);
             workspace::init_settings(cx);
             editor::init_settings(cx);
-            theme::init(theme::LoadThemes::JustBase, cx);
+            theme::ThemeSettings::register(cx)
         });
     }
 

crates/go_to_line/src/cursor_position.rs 🔗

@@ -304,7 +304,7 @@ impl From<settings::LineIndicatorFormat> for LineIndicatorFormat {
 }
 
 impl Settings for LineIndicatorFormat {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         content.line_indicator_format.unwrap().into()
     }
 }

crates/image_viewer/src/image_viewer_settings.rs 🔗

@@ -1,3 +1,4 @@
+use gpui::App;
 pub use settings::ImageFileSizeUnit;
 use settings::Settings;
 
@@ -11,7 +12,7 @@ pub struct ImageViewerSettings {
 }
 
 impl Settings for ImageViewerSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         Self {
             unit: content.image_viewer.clone().unwrap().unit.unwrap(),
         }

crates/journal/src/journal.rs 🔗

@@ -33,7 +33,7 @@ pub struct JournalSettings {
 }
 
 impl settings::Settings for JournalSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         let journal = content.journal.clone().unwrap();
 
         Self {

crates/language/src/language_settings.rs 🔗

@@ -500,7 +500,7 @@ fn merge_with_editorconfig(settings: &mut LanguageSettings, cfg: &EditorconfigPr
 }
 
 impl settings::Settings for AllLanguageSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         let all_languages = &content.project.all_languages;
 
         fn load_from_content(settings: LanguageSettingsContent) -> LanguageSettings {

crates/language_models/src/settings.rs 🔗

@@ -36,7 +36,7 @@ pub struct AllLanguageModelSettings {
 impl settings::Settings for AllLanguageModelSettings {
     const PRESERVED_KEYS: Option<&'static [&'static str]> = Some(&["version"]);
 
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         let language_models = content.language_models.clone().unwrap();
         let anthropic = language_models.anthropic.unwrap();
         let bedrock = language_models.bedrock.unwrap();

crates/onboarding/src/basics_page.rs 🔗

@@ -34,8 +34,16 @@ fn get_theme_family_themes(theme_name: &str) -> Option<(&'static str, &'static s
 }
 
 fn render_theme_section(tab_index: &mut isize, cx: &mut App) -> impl IntoElement {
-    let theme_selection = ThemeSettings::get_global(cx).theme.clone();
+    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 {
+        mode: match *system_appearance {
+            Appearance::Light => ThemeMode::Light,
+            Appearance::Dark => ThemeMode::Dark,
+        },
+        light: ThemeName("One Light".into()),
+        dark: ThemeName("One Dark".into()),
+    });
 
     let theme_mode = theme_selection
         .mode()
@@ -103,7 +111,7 @@ fn render_theme_section(tab_index: &mut isize, cx: &mut App) -> impl IntoElement
             ThemeMode::Dark => Appearance::Dark,
             ThemeMode::System => *system_appearance,
         };
-        let current_theme_name: SharedString = theme_selection.name(appearance).0.into();
+        let current_theme_name = SharedString::new(theme_selection.theme(appearance));
 
         let theme_names = match appearance {
             Appearance::Light => LIGHT_THEMES,

crates/outline_panel/src/outline_panel_settings.rs 🔗

@@ -41,7 +41,7 @@ impl ScrollbarVisibility for OutlinePanelSettings {
 }
 
 impl Settings for OutlinePanelSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         let panel = content.outline_panel.as_ref().unwrap();
         Self {
             button: panel.button.unwrap(),

crates/project/src/agent_server_store.rs 🔗

@@ -14,7 +14,7 @@ use feature_flags::FeatureFlagAppExt as _;
 use fs::{Fs, RemoveOptions, RenameOptions};
 use futures::StreamExt as _;
 use gpui::{
-    AppContext as _, AsyncApp, Context, Entity, EventEmitter, SharedString, Subscription, Task,
+    App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, SharedString, Subscription, Task,
 };
 use http_client::github::AssetKind;
 use node_runtime::NodeRuntime;
@@ -22,7 +22,7 @@ use remote::RemoteClient;
 use rpc::{AnyProtoClient, TypedEnvelope, proto};
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
-use settings::SettingsStore;
+use settings::{SettingsContent, SettingsStore};
 use util::{ResultExt as _, debug_panic};
 
 use crate::ProjectEnvironment;
@@ -1294,7 +1294,7 @@ impl From<settings::CustomAgentServerSettings> for CustomAgentServerSettings {
 }
 
 impl settings::Settings for AllAgentServersSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         let agent_settings = content.agent_servers.clone().unwrap();
         Self {
             gemini: agent_settings.gemini.map(Into::into),
@@ -1307,4 +1307,6 @@ impl settings::Settings for AllAgentServersSettings {
                 .collect(),
         }
     }
+
+    fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut SettingsContent) {}
 }

crates/project/src/project.rs 🔗

@@ -980,7 +980,7 @@ pub struct DisableAiSettings {
 }
 
 impl settings::Settings for DisableAiSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         Self {
             disable_ai: content.disable_ai.unwrap().0,
         }

crates/project/src/project_settings.rs 🔗

@@ -4,7 +4,7 @@ use context_server::ContextServerCommand;
 use dap::adapters::DebugAdapterName;
 use fs::Fs;
 use futures::StreamExt as _;
-use gpui::{AsyncApp, BorrowAppContext, Context, Entity, EventEmitter, Subscription, Task};
+use gpui::{App, AsyncApp, BorrowAppContext, Context, Entity, EventEmitter, Subscription, Task};
 use lsp::LanguageServerName;
 use paths::{
     EDITORCONFIG_NAME, local_debug_file_relative_path, local_settings_file_relative_path,
@@ -437,7 +437,7 @@ pub struct LspPullDiagnosticsSettings {
 }
 
 impl Settings for ProjectSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         let project = &content.project.clone();
         let diagnostics = content.diagnostics.as_ref().unwrap();
         let lsp_pull_diagnostics = diagnostics.lsp_pull_diagnostics.as_ref().unwrap();

crates/project_panel/src/project_panel_settings.rs 🔗

@@ -55,7 +55,7 @@ impl ScrollbarVisibility for ProjectPanelSettings {
 }
 
 impl Settings for ProjectPanelSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut ui::App) -> Self {
         let project_panel = content.project_panel.clone().unwrap();
         Self {
             button: project_panel.button.unwrap(),

crates/recent_projects/src/remote_connections.rs 🔗

@@ -104,7 +104,7 @@ impl From<WslConnection> for Connection {
 }
 
 impl Settings for SshSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         let remote = &content.remote;
         Self {
             ssh_connections: remote.ssh_connections.clone().unwrap_or_default().into(),

crates/repl/src/jupyter_settings.rs 🔗

@@ -19,7 +19,7 @@ impl JupyterSettings {
 }
 
 impl Settings for JupyterSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         let jupyter = content.editor.jupyter.clone().unwrap();
         Self {
             kernel_selections: jupyter.kernel_selections.unwrap_or_default(),

crates/repl/src/repl_settings.rs 🔗

@@ -1,3 +1,4 @@
+use gpui::App;
 use settings::Settings;
 
 /// Settings for configuring REPL display and behavior.
@@ -16,7 +17,7 @@ pub struct ReplSettings {
 }
 
 impl Settings for ReplSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         let repl = content.repl.as_ref().unwrap();
 
         Self {

crates/settings/src/base_keymap_setting.rs 🔗

@@ -4,6 +4,7 @@ use crate::{
     self as settings,
     settings_content::{BaseKeymapContent, SettingsContent},
 };
+use gpui::App;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use settings::{Settings, VsCodeSettings};
@@ -130,7 +131,7 @@ impl BaseKeymap {
 }
 
 impl Settings for BaseKeymap {
-    fn from_settings(s: &crate::settings_content::SettingsContent) -> Self {
+    fn from_settings(s: &crate::settings_content::SettingsContent, _cx: &mut App) -> Self {
         s.base_keymap.unwrap().into()
     }
 

crates/settings/src/settings_store.rs 🔗

@@ -67,7 +67,11 @@ pub trait Settings: 'static + Send + Sync + Sized {
     ///
     /// This function *should* panic if default values are missing,
     /// and you should add a default to default.json for documentation.
-    fn from_settings(content: &SettingsContent) -> Self;
+    fn from_settings(content: &SettingsContent, cx: &mut App) -> Self;
+
+    fn missing_default() -> anyhow::Error {
+        anyhow::anyhow!("missing default for: {}", std::any::type_name::<Self>())
+    }
 
     /// Use [the helpers in the vscode_import module](crate::vscode_import) to apply known
     /// equivalent settings from a vscode config to our config
@@ -78,8 +82,8 @@ pub trait Settings: 'static + Send + Sync + Sized {
     where
         Self: Sized,
     {
-        SettingsStore::update_global(cx, |store, _| {
-            store.register_setting::<Self>();
+        SettingsStore::update_global(cx, |store, cx| {
+            store.register_setting::<Self>(cx);
         });
     }
 
@@ -201,7 +205,7 @@ struct SettingValue<T> {
 trait AnySettingValue: 'static + Send + Sync {
     fn setting_type_name(&self) -> &'static str;
 
-    fn from_settings(&self, s: &SettingsContent) -> Box<dyn Any>;
+    fn from_settings(&self, s: &SettingsContent, cx: &mut App) -> Box<dyn Any>;
 
     fn value_for_path(&self, path: Option<SettingsLocation>) -> &dyn Any;
     fn all_local_values(&self) -> Vec<(WorktreeId, Arc<RelPath>, &dyn Any)>;
@@ -255,7 +259,7 @@ impl SettingsStore {
     }
 
     /// Add a new type of setting to the store.
-    pub fn register_setting<T: Settings>(&mut self) {
+    pub fn register_setting<T: Settings>(&mut self, cx: &mut App) {
         let setting_type_id = TypeId::of::<T>();
         let entry = self.setting_values.entry(setting_type_id);
 
@@ -267,7 +271,7 @@ impl SettingsStore {
             global_value: None,
             local_values: Vec::new(),
         }));
-        let value = T::from_settings(&self.merged_settings);
+        let value = T::from_settings(&self.merged_settings, cx);
         setting_value.set_global_value(Box::new(value));
     }
 
@@ -940,7 +944,7 @@ impl SettingsStore {
             self.merged_settings = Rc::new(merged);
 
             for setting_value in self.setting_values.values_mut() {
-                let value = setting_value.from_settings(&self.merged_settings);
+                let value = setting_value.from_settings(&self.merged_settings, cx);
                 setting_value.set_global_value(value);
             }
         }
@@ -977,7 +981,8 @@ impl SettingsStore {
             }
 
             for setting_value in self.setting_values.values_mut() {
-                let value = setting_value.from_settings(&project_settings_stack.last().unwrap());
+                let value =
+                    setting_value.from_settings(&project_settings_stack.last().unwrap(), cx);
                 setting_value.set_local_value(*root_id, directory_path.clone(), value);
             }
         }
@@ -1061,8 +1066,8 @@ impl Debug for SettingsStore {
 }
 
 impl<T: Settings> AnySettingValue for SettingValue<T> {
-    fn from_settings(&self, s: &SettingsContent) -> Box<dyn Any> {
-        Box::new(T::from_settings(s)) as _
+    fn from_settings(&self, s: &SettingsContent, cx: &mut App) -> Box<dyn Any> {
+        Box::new(T::from_settings(s, cx)) as _
     }
 
     fn setting_type_name(&self) -> &'static str {
@@ -1133,7 +1138,7 @@ mod tests {
     }
 
     impl Settings for AutoUpdateSetting {
-        fn from_settings(content: &SettingsContent) -> Self {
+        fn from_settings(content: &SettingsContent, _: &mut App) -> Self {
             AutoUpdateSetting {
                 auto_update: content.auto_update.unwrap(),
             }
@@ -1147,7 +1152,7 @@ mod tests {
     }
 
     impl Settings for ItemSettings {
-        fn from_settings(content: &SettingsContent) -> Self {
+        fn from_settings(content: &SettingsContent, _: &mut App) -> Self {
             let content = content.tabs.clone().unwrap();
             ItemSettings {
                 close_position: content.close_position.unwrap(),
@@ -1176,7 +1181,7 @@ mod tests {
     }
 
     impl Settings for DefaultLanguageSettings {
-        fn from_settings(content: &SettingsContent) -> Self {
+        fn from_settings(content: &SettingsContent, _: &mut App) -> Self {
             let content = &content.project.all_languages.defaults;
             DefaultLanguageSettings {
                 tab_size: content.tab_size.unwrap(),
@@ -1200,9 +1205,9 @@ mod tests {
     #[gpui::test]
     fn test_settings_store_basic(cx: &mut App) {
         let mut store = SettingsStore::new(cx, &default_settings());
-        store.register_setting::<AutoUpdateSetting>();
-        store.register_setting::<ItemSettings>();
-        store.register_setting::<DefaultLanguageSettings>();
+        store.register_setting::<AutoUpdateSetting>(cx);
+        store.register_setting::<ItemSettings>(cx);
+        store.register_setting::<DefaultLanguageSettings>(cx);
 
         assert_eq!(
             store.get::<AutoUpdateSetting>(None),
@@ -1308,7 +1313,7 @@ mod tests {
         store
             .set_user_settings(r#"{ "auto_update": false }"#, cx)
             .unwrap();
-        store.register_setting::<AutoUpdateSetting>();
+        store.register_setting::<AutoUpdateSetting>(cx);
 
         assert_eq!(
             store.get::<AutoUpdateSetting>(None),
@@ -1516,9 +1521,9 @@ mod tests {
     #[gpui::test]
     fn test_vscode_import(cx: &mut App) {
         let mut store = SettingsStore::new(cx, &test_settings());
-        store.register_setting::<DefaultLanguageSettings>();
-        store.register_setting::<ItemSettings>();
-        store.register_setting::<AutoUpdateSetting>();
+        store.register_setting::<DefaultLanguageSettings>(cx);
+        store.register_setting::<ItemSettings>(cx);
+        store.register_setting::<AutoUpdateSetting>(cx);
 
         // create settings that werent present
         check_vscode_import(
@@ -1637,7 +1642,7 @@ mod tests {
     #[gpui::test]
     fn test_global_settings(cx: &mut App) {
         let mut store = SettingsStore::new(cx, &test_settings());
-        store.register_setting::<ItemSettings>();
+        store.register_setting::<ItemSettings>(cx);
 
         // Set global settings - these should override defaults but not user settings
         store
@@ -1686,7 +1691,7 @@ mod tests {
     #[gpui::test]
     fn test_get_value_for_field_basic(cx: &mut App) {
         let mut store = SettingsStore::new(cx, &test_settings());
-        store.register_setting::<DefaultLanguageSettings>();
+        store.register_setting::<DefaultLanguageSettings>(cx);
 
         store
             .set_user_settings(r#"{"preferred_line_length": 0}"#, cx)
@@ -1743,8 +1748,8 @@ mod tests {
     #[gpui::test]
     fn test_get_value_for_field_local_worktrees_dont_interfere(cx: &mut App) {
         let mut store = SettingsStore::new(cx, &test_settings());
-        store.register_setting::<DefaultLanguageSettings>();
-        store.register_setting::<AutoUpdateSetting>();
+        store.register_setting::<DefaultLanguageSettings>(cx);
+        store.register_setting::<AutoUpdateSetting>(cx);
 
         let local_1 = (WorktreeId::from_usize(0), RelPath::empty().into_arc());
 
@@ -1872,7 +1877,7 @@ mod tests {
     #[gpui::test]
     fn test_get_overrides_for_field(cx: &mut App) {
         let mut store = SettingsStore::new(cx, &test_settings());
-        store.register_setting::<DefaultLanguageSettings>();
+        store.register_setting::<DefaultLanguageSettings>(cx);
 
         let wt0_root = (WorktreeId::from_usize(0), RelPath::empty().into_arc());
         let wt0_child1 = (WorktreeId::from_usize(0), rel_path("child1").into_arc());

crates/storybook/src/storybook.rs 🔗

@@ -19,7 +19,7 @@ use reqwest_client::ReqwestClient;
 use settings::{KeymapFile, Settings};
 use simplelog::SimpleLogger;
 use strum::IntoEnumIterator;
-use theme::ThemeSettings;
+use theme::{ThemeRegistry, ThemeSettings};
 use ui::prelude::*;
 use workspace;
 
@@ -80,9 +80,9 @@ fn main() {
 
         let selector = story_selector;
 
+        let theme_registry = ThemeRegistry::global(cx);
         let mut theme_settings = ThemeSettings::get_global(cx).clone();
-        theme_settings.theme =
-            theme::ThemeSelection::Static(settings::ThemeName(theme_name.into()));
+        theme_settings.active_theme = theme_registry.get(&theme_name).unwrap();
         ThemeSettings::override_global(theme_settings, cx);
 
         language::init(cx);

crates/terminal/src/terminal_settings.rs 🔗

@@ -2,7 +2,7 @@ use alacritty_terminal::vte::ansi::{
     CursorShape as AlacCursorShape, CursorStyle as AlacCursorStyle,
 };
 use collections::HashMap;
-use gpui::{FontFallbacks, FontFeatures, FontWeight, Pixels, px};
+use gpui::{App, FontFallbacks, FontFeatures, FontWeight, Pixels, px};
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 
@@ -72,7 +72,7 @@ fn settings_shell_to_task_shell(shell: settings::Shell) -> Shell {
 }
 
 impl settings::Settings for TerminalSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         let content = content.terminal.clone().unwrap();
         TerminalSettings {
             shell: settings_shell_to_task_shell(content.shell.unwrap()),

crates/theme/src/fallback_themes.rs 🔗

@@ -3,9 +3,9 @@ use std::sync::Arc;
 use gpui::{FontStyle, FontWeight, HighlightStyle, Hsla, WindowBackgroundAppearance, hsla};
 
 use crate::{
-    AccentColors, Appearance, DEFAULT_DARK_THEME, PlayerColors, StatusColors,
-    StatusColorsRefinement, SyntaxTheme, SystemColors, Theme, ThemeColors, ThemeColorsRefinement,
-    ThemeFamily, ThemeStyles, default_color_scales,
+    AccentColors, Appearance, PlayerColors, StatusColors, StatusColorsRefinement, SyntaxTheme,
+    SystemColors, Theme, ThemeColors, ThemeColorsRefinement, ThemeFamily, ThemeStyles,
+    default_color_scales,
 };
 
 /// The default theme family for Zed.
@@ -92,7 +92,7 @@ pub(crate) fn zed_default_dark() -> Theme {
     let player = PlayerColors::dark();
     Theme {
         id: "one_dark".to_string(),
-        name: DEFAULT_DARK_THEME.into(),
+        name: "One Dark".into(),
         appearance: Appearance::Dark,
         styles: ThemeStyles {
             window_background_appearance: WindowBackgroundAppearance::Opaque,

crates/theme/src/settings.rs 🔗

@@ -1,6 +1,8 @@
+use crate::fallback_themes::zed_default_dark;
 use crate::{
-    Appearance, DEFAULT_ICON_THEME_NAME, SyntaxTheme, Theme, status_colors_refinement,
-    syntax_overrides, theme_colors_refinement,
+    Appearance, DEFAULT_ICON_THEME_NAME, IconTheme, IconThemeNotFoundError, SyntaxTheme, Theme,
+    ThemeNotFoundError, ThemeRegistry, status_colors_refinement, syntax_overrides,
+    theme_colors_refinement,
 };
 use collections::HashMap;
 use derive_more::{Deref, DerefMut};
@@ -14,6 +16,7 @@ use serde::{Deserialize, Serialize};
 pub use settings::{FontFamilyName, IconThemeName, ThemeMode, ThemeName};
 use settings::{Settings, SettingsContent};
 use std::sync::Arc;
+use util::ResultExt as _;
 
 const MIN_FONT_SIZE: Pixels = px(6.0);
 const MAX_FONT_SIZE: Pixels = px(100.0);
@@ -122,7 +125,9 @@ pub struct ThemeSettings {
     /// The terminal font family can be overridden using it's own setting.
     pub buffer_line_height: BufferLineHeight,
     /// The current theme selection.
-    pub theme: ThemeSelection,
+    pub theme_selection: Option<ThemeSelection>,
+    /// The active theme.
+    pub active_theme: Arc<Theme>,
     /// Manual overrides for the active theme.
     ///
     /// Note: This setting is still experimental. See [this tracking issue](https://github.com/zed-industries/zed/issues/18078)
@@ -130,7 +135,9 @@ pub struct ThemeSettings {
     /// Manual overrides per theme
     pub theme_overrides: HashMap<String, settings::ThemeStyleContent>,
     /// The current icon theme selection.
-    pub icon_theme: IconThemeSelection,
+    pub icon_theme_selection: Option<IconThemeSelection>,
+    /// The active icon theme.
+    pub active_icon_theme: Arc<IconTheme>,
     /// The density of the UI.
     /// Note: This setting is still experimental. See [this tracking issue](
     pub ui_density: UiDensity,
@@ -138,14 +145,73 @@ pub struct ThemeSettings {
     pub unnecessary_code_fade: f32,
 }
 
-pub(crate) const DEFAULT_LIGHT_THEME: &'static str = "One Light";
-pub(crate) const DEFAULT_DARK_THEME: &'static str = "One Dark";
+impl ThemeSettings {
+    const DEFAULT_LIGHT_THEME: &'static str = "One Light";
+    const DEFAULT_DARK_THEME: &'static str = "One Dark";
+
+    /// Returns the name of the default theme for the given [`Appearance`].
+    pub fn default_theme(appearance: Appearance) -> &'static str {
+        match appearance {
+            Appearance::Light => Self::DEFAULT_LIGHT_THEME,
+            Appearance::Dark => Self::DEFAULT_DARK_THEME,
+        }
+    }
 
-/// Returns the name of the default theme for the given [`Appearance`].
-pub fn default_theme(appearance: Appearance) -> &'static str {
-    match appearance {
-        Appearance::Light => DEFAULT_LIGHT_THEME,
-        Appearance::Dark => DEFAULT_DARK_THEME,
+    /// Reloads the current theme.
+    ///
+    /// Reads the [`ThemeSettings`] to know which theme should be loaded,
+    /// taking into account the current [`SystemAppearance`].
+    pub fn reload_current_theme(cx: &mut App) {
+        let mut theme_settings = ThemeSettings::get_global(cx).clone();
+        let system_appearance = SystemAppearance::global(cx);
+
+        if let Some(theme_selection) = theme_settings.theme_selection.clone() {
+            let mut theme_name = theme_selection.theme(*system_appearance);
+
+            // If the selected theme doesn't exist, fall back to a default theme
+            // based on the system appearance.
+            let theme_registry = ThemeRegistry::global(cx);
+            if let Err(err @ ThemeNotFoundError(_)) = theme_registry.get(theme_name) {
+                if theme_registry.extensions_loaded() {
+                    log::error!("{err}");
+                }
+
+                theme_name = Self::default_theme(*system_appearance);
+            };
+
+            if let Some(_theme) = theme_settings.switch_theme(theme_name, cx) {
+                ThemeSettings::override_global(theme_settings, cx);
+            }
+        }
+    }
+
+    /// Reloads the current icon theme.
+    ///
+    /// Reads the [`ThemeSettings`] to know which icon theme should be loaded,
+    /// taking into account the current [`SystemAppearance`].
+    pub fn reload_current_icon_theme(cx: &mut App) {
+        let mut theme_settings = ThemeSettings::get_global(cx).clone();
+        let system_appearance = SystemAppearance::global(cx);
+
+        if let Some(icon_theme_selection) = theme_settings.icon_theme_selection.clone() {
+            let mut icon_theme_name = icon_theme_selection.icon_theme(*system_appearance);
+
+            // If the selected icon theme doesn't exist, fall back to the default theme.
+            let theme_registry = ThemeRegistry::global(cx);
+            if let Err(err @ IconThemeNotFoundError(_)) =
+                theme_registry.get_icon_theme(icon_theme_name)
+            {
+                if theme_registry.extensions_loaded() {
+                    log::error!("{err}");
+                }
+
+                icon_theme_name = DEFAULT_ICON_THEME_NAME;
+            };
+
+            if let Some(_theme) = theme_settings.switch_icon_theme(icon_theme_name, cx) {
+                ThemeSettings::override_global(theme_settings, cx);
+            }
+        }
     }
 }
 
@@ -171,6 +237,13 @@ impl SystemAppearance {
             GlobalSystemAppearance(SystemAppearance(cx.window_appearance().into()));
     }
 
+    /// Returns the global [`SystemAppearance`].
+    ///
+    /// Inserts a default [`SystemAppearance`] if one does not yet exist.
+    pub(crate) fn default_global(cx: &mut App) -> Self {
+        cx.default_global::<GlobalSystemAppearance>().0
+    }
+
     /// Returns the global [`SystemAppearance`].
     pub fn global(cx: &App) -> Self {
         cx.global::<GlobalSystemAppearance>().0
@@ -229,15 +302,15 @@ impl From<settings::ThemeSelection> for ThemeSelection {
 
 impl ThemeSelection {
     /// Returns the theme name for the selected [ThemeMode].
-    pub fn name(&self, system_appearance: Appearance) -> ThemeName {
+    pub fn theme(&self, system_appearance: Appearance) -> &str {
         match self {
-            Self::Static(theme) => theme.clone(),
+            Self::Static(theme) => &theme.0,
             Self::Dynamic { mode, light, dark } => match mode {
-                ThemeMode::Light => light.clone(),
-                ThemeMode::Dark => dark.clone(),
+                ThemeMode::Light => &light.0,
+                ThemeMode::Dark => &dark.0,
                 ThemeMode::System => match system_appearance {
-                    Appearance::Light => light.clone(),
-                    Appearance::Dark => dark.clone(),
+                    Appearance::Light => &light.0,
+                    Appearance::Dark => &dark.0,
                 },
             },
         }
@@ -281,15 +354,15 @@ impl From<settings::IconThemeSelection> for IconThemeSelection {
 
 impl IconThemeSelection {
     /// Returns the icon theme name based on the given [`Appearance`].
-    pub fn name(&self, system_appearance: Appearance) -> IconThemeName {
+    pub fn icon_theme(&self, system_appearance: Appearance) -> &str {
         match self {
-            Self::Static(theme) => theme.clone(),
+            Self::Static(theme) => &theme.0,
             Self::Dynamic { mode, light, dark } => match mode {
-                ThemeMode::Light => light.clone(),
-                ThemeMode::Dark => dark.clone(),
+                ThemeMode::Light => &light.0,
+                ThemeMode::Dark => &dark.0,
                 ThemeMode::System => match system_appearance {
-                    Appearance::Light => light.clone(),
-                    Appearance::Dark => dark.clone(),
+                    Appearance::Light => &light.0,
+                    Appearance::Dark => &dark.0,
                 },
             },
         }
@@ -335,7 +408,7 @@ pub fn set_theme(
 /// Sets the icon theme for the given appearance to the icon theme with the specified name.
 pub fn set_icon_theme(
     current: &mut SettingsContent,
-    icon_theme_name: IconThemeName,
+    icon_theme_name: String,
     appearance: Appearance,
 ) {
     if let Some(selection) = current.theme.icon_theme.as_mut() {
@@ -351,9 +424,11 @@ pub fn set_icon_theme(
             },
         };
 
-        *icon_theme_to_update = icon_theme_name;
+        *icon_theme_to_update = IconThemeName(icon_theme_name.into());
     } else {
-        current.theme.icon_theme = Some(settings::IconThemeSelection::Static(icon_theme_name));
+        current.theme.icon_theme = Some(settings::IconThemeSelection::Static(IconThemeName(
+            icon_theme_name.into(),
+        )));
     }
 }
 
@@ -381,8 +456,8 @@ pub fn set_mode(content: &mut SettingsContent, mode: ThemeMode) {
     } else {
         theme.theme = Some(settings::ThemeSelection::Dynamic {
             mode,
-            light: ThemeName(DEFAULT_LIGHT_THEME.into()),
-            dark: ThemeName(DEFAULT_DARK_THEME.into()),
+            light: ThemeName(ThemeSettings::DEFAULT_LIGHT_THEME.into()),
+            dark: ThemeName(ThemeSettings::DEFAULT_DARK_THEME.into()),
         });
     }
 
@@ -521,22 +596,44 @@ impl ThemeSettings {
         f32::max(self.buffer_line_height.value(), MIN_LINE_HEIGHT)
     }
 
+    /// Switches to the theme with the given name, if it exists.
+    ///
+    /// Returns a `Some` containing the new theme if it was successful.
+    /// Returns `None` otherwise.
+    pub fn switch_theme(&mut self, theme: &str, cx: &mut App) -> Option<Arc<Theme>> {
+        let themes = ThemeRegistry::default_global(cx);
+
+        let mut new_theme = None;
+
+        match themes.get(theme) {
+            Ok(theme) => {
+                self.active_theme = theme.clone();
+                new_theme = Some(theme);
+            }
+            Err(err @ ThemeNotFoundError(_)) => {
+                log::error!("{err}");
+            }
+        }
+
+        self.apply_theme_overrides();
+
+        new_theme
+    }
+
     /// Applies the theme overrides, if there are any, to the current theme.
-    pub fn apply_theme_overrides(&self, mut arc_theme: Arc<Theme>) -> Arc<Theme> {
+    pub fn apply_theme_overrides(&mut self) {
         // Apply the old overrides setting first, so that the new setting can override those.
         if let Some(experimental_theme_overrides) = &self.experimental_theme_overrides {
-            let mut theme = (*arc_theme).clone();
+            let mut theme = (*self.active_theme).clone();
             ThemeSettings::modify_theme(&mut theme, experimental_theme_overrides);
-            arc_theme = Arc::new(theme);
+            self.active_theme = Arc::new(theme);
         }
 
-        if let Some(theme_overrides) = self.theme_overrides.get(arc_theme.name.as_ref()) {
-            let mut theme = (*arc_theme).clone();
+        if let Some(theme_overrides) = self.theme_overrides.get(self.active_theme.name.as_ref()) {
+            let mut theme = (*self.active_theme).clone();
             ThemeSettings::modify_theme(&mut theme, theme_overrides);
-            arc_theme = Arc::new(theme);
+            self.active_theme = Arc::new(theme);
         }
-
-        arc_theme
     }
 
     fn modify_theme(base_theme: &mut Theme, theme_overrides: &settings::ThemeStyleContent) {
@@ -557,6 +654,24 @@ impl ThemeSettings {
             syntax_overrides(&theme_overrides),
         );
     }
+
+    /// Switches to the icon theme with the given name, if it exists.
+    ///
+    /// Returns a `Some` containing the new icon theme if it was successful.
+    /// Returns `None` otherwise.
+    pub fn switch_icon_theme(&mut self, icon_theme: &str, cx: &mut App) -> Option<Arc<IconTheme>> {
+        let themes = ThemeRegistry::default_global(cx);
+
+        let mut new_icon_theme = None;
+
+        if let Some(icon_theme) = themes.get_icon_theme(icon_theme).log_err() {
+            self.active_icon_theme = icon_theme.clone();
+            new_icon_theme = Some(icon_theme);
+            cx.refresh_windows();
+        }
+
+        new_icon_theme
+    }
 }
 
 /// Observe changes to the adjusted buffer font size.
@@ -689,11 +804,14 @@ pub fn font_fallbacks_from_settings(
 }
 
 impl settings::Settings for ThemeSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, cx: &mut App) -> Self {
         let content = &content.theme;
+        // todo(settings_refactor). This should *not* require cx...
+        let themes = ThemeRegistry::default_global(cx);
+        let system_appearance = SystemAppearance::default_global(cx);
         let theme_selection: ThemeSelection = content.theme.clone().unwrap().into();
         let icon_theme_selection: IconThemeSelection = content.icon_theme.clone().unwrap().into();
-        Self {
+        let mut this = Self {
             ui_font_size: clamp_font_size(content.ui_font_size.unwrap().into()),
             ui_font: Font {
                 family: content.ui_font_family.as_ref().unwrap().0.clone().into(),
@@ -719,13 +837,23 @@ impl settings::Settings for ThemeSettings {
             buffer_line_height: content.buffer_line_height.unwrap().into(),
             agent_ui_font_size: content.agent_ui_font_size.map(Into::into),
             agent_buffer_font_size: content.agent_buffer_font_size.map(Into::into),
-            theme: theme_selection,
+            active_theme: themes
+                .get(theme_selection.theme(*system_appearance))
+                .or(themes.get(&zed_default_dark().name))
+                .unwrap(),
+            theme_selection: Some(theme_selection),
             experimental_theme_overrides: content.experimental_theme_overrides.clone(),
             theme_overrides: content.theme_overrides.clone(),
-            icon_theme: icon_theme_selection,
+            active_icon_theme: themes
+                .get_icon_theme(icon_theme_selection.icon_theme(*system_appearance))
+                .or_else(|_| themes.default_icon_theme())
+                .unwrap(),
+            icon_theme_selection: Some(icon_theme_selection),
             ui_density: content.ui_density.unwrap_or_default().into(),
             unnecessary_code_fade: content.unnecessary_code_fade.unwrap().0.clamp(0.0, 0.9),
-        }
+        };
+        this.apply_theme_overrides();
+        this
     }
 
     fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut SettingsContent) {

crates/theme/src/theme.rs 🔗

@@ -27,8 +27,6 @@ use ::settings::SettingsStore;
 use anyhow::Result;
 use fallback_themes::apply_status_color_defaults;
 use fs::Fs;
-use gpui::BorrowAppContext;
-use gpui::Global;
 use gpui::{
     App, AssetSource, HighlightStyle, Hsla, Pixels, Refineable, SharedString, WindowAppearance,
     WindowBackgroundAppearance, px,
@@ -97,7 +95,6 @@ pub enum LoadThemes {
 
 /// Initialize the theme system.
 pub fn init(themes_to_load: LoadThemes, cx: &mut App) {
-    SystemAppearance::init(cx);
     let (assets, load_user_themes) = match themes_to_load {
         LoadThemes::JustBase => (Box::new(()) as Box<dyn AssetSource>, false),
         LoadThemes::All(assets) => (assets, true),
@@ -111,67 +108,40 @@ pub fn init(themes_to_load: LoadThemes, cx: &mut App) {
     ThemeSettings::register(cx);
     FontFamilyCache::init_global(cx);
 
-    let theme = GlobalTheme::configured_theme(cx);
-    let icon_theme = GlobalTheme::configured_icon_theme(cx);
-    cx.set_global(GlobalTheme { theme, icon_theme });
-
-    let settings = ThemeSettings::get_global(cx);
-
-    let mut prev_buffer_font_size_settings = settings.buffer_font_size_settings();
-    let mut prev_ui_font_size_settings = settings.ui_font_size_settings();
-    let mut prev_agent_ui_font_size_settings = settings.agent_ui_font_size_settings();
-    let mut prev_agent_buffer_font_size_settings = settings.agent_buffer_font_size_settings();
-    let mut prev_theme_name = settings.theme.name(SystemAppearance::global(cx).0);
-    let mut prev_icon_theme_name = settings.icon_theme.name(SystemAppearance::global(cx).0);
-    let mut prev_theme_overrides = (
-        settings.experimental_theme_overrides.clone(),
-        settings.theme_overrides.clone(),
-    );
+    let mut prev_buffer_font_size_settings =
+        ThemeSettings::get_global(cx).buffer_font_size_settings();
+    let mut prev_ui_font_size_settings = ThemeSettings::get_global(cx).ui_font_size_settings();
+    let mut prev_agent_ui_font_size_settings =
+        ThemeSettings::get_global(cx).agent_ui_font_size_settings();
+    let mut prev_agent_buffer_font_size_settings =
+        ThemeSettings::get_global(cx).agent_buffer_font_size_settings();
 
     cx.observe_global::<SettingsStore>(move |cx| {
-        let settings = ThemeSettings::get_global(cx);
-
-        let buffer_font_size_settings = settings.buffer_font_size_settings();
-        let ui_font_size_settings = settings.ui_font_size_settings();
-        let agent_ui_font_size_settings = settings.agent_ui_font_size_settings();
-        let agent_buffer_font_size_settings = settings.agent_buffer_font_size_settings();
-        let theme_name = settings.theme.name(SystemAppearance::global(cx).0);
-        let icon_theme_name = settings.icon_theme.name(SystemAppearance::global(cx).0);
-        let theme_overrides = (
-            settings.experimental_theme_overrides.clone(),
-            settings.theme_overrides.clone(),
-        );
-
+        let buffer_font_size_settings = ThemeSettings::get_global(cx).buffer_font_size_settings();
         if buffer_font_size_settings != prev_buffer_font_size_settings {
             prev_buffer_font_size_settings = buffer_font_size_settings;
             reset_buffer_font_size(cx);
         }
 
+        let ui_font_size_settings = ThemeSettings::get_global(cx).ui_font_size_settings();
         if ui_font_size_settings != prev_ui_font_size_settings {
             prev_ui_font_size_settings = ui_font_size_settings;
             reset_ui_font_size(cx);
         }
 
+        let agent_ui_font_size_settings =
+            ThemeSettings::get_global(cx).agent_ui_font_size_settings();
         if agent_ui_font_size_settings != prev_agent_ui_font_size_settings {
             prev_agent_ui_font_size_settings = agent_ui_font_size_settings;
             reset_agent_ui_font_size(cx);
         }
 
+        let agent_buffer_font_size_settings =
+            ThemeSettings::get_global(cx).agent_buffer_font_size_settings();
         if agent_buffer_font_size_settings != prev_agent_buffer_font_size_settings {
             prev_agent_buffer_font_size_settings = agent_buffer_font_size_settings;
             reset_agent_buffer_font_size(cx);
         }
-
-        if theme_name != prev_theme_name || theme_overrides != prev_theme_overrides {
-            prev_theme_name = theme_name;
-            prev_theme_overrides = theme_overrides;
-            GlobalTheme::reload_theme(cx);
-        }
-
-        if icon_theme_name != prev_icon_theme_name {
-            prev_icon_theme_name = icon_theme_name;
-            GlobalTheme::reload_icon_theme(cx);
-        }
     })
     .detach();
 }
@@ -184,7 +154,7 @@ pub trait ActiveTheme {
 
 impl ActiveTheme for App {
     fn theme(&self) -> &Arc<Theme> {
-        GlobalTheme::theme(self)
+        &ThemeSettings::get_global(self).active_theme
     }
 }
 
@@ -438,82 +408,3 @@ pub async fn read_icon_theme(
 
     Ok(icon_theme_family)
 }
-
-/// The active theme
-pub struct GlobalTheme {
-    theme: Arc<Theme>,
-    icon_theme: Arc<IconTheme>,
-}
-impl Global for GlobalTheme {}
-
-impl GlobalTheme {
-    fn configured_theme(cx: &mut App) -> Arc<Theme> {
-        let themes = ThemeRegistry::default_global(cx);
-        let theme_settings = ThemeSettings::get_global(cx);
-        let system_appearance = SystemAppearance::global(cx);
-
-        let theme_name = theme_settings.theme.name(*system_appearance);
-
-        let theme = match themes.get(&theme_name.0) {
-            Ok(theme) => theme,
-            Err(err) => {
-                if themes.extensions_loaded() {
-                    log::error!("{err}");
-                }
-                themes
-                    .get(default_theme(*system_appearance))
-                    // fallback for tests.
-                    .unwrap_or_else(|_| themes.get(DEFAULT_DARK_THEME).unwrap())
-            }
-        };
-        theme_settings.apply_theme_overrides(theme)
-    }
-
-    /// Reloads the current theme.
-    ///
-    /// Reads the [`ThemeSettings`] to know which theme should be loaded,
-    /// taking into account the current [`SystemAppearance`].
-    pub fn reload_theme(cx: &mut App) {
-        let theme = Self::configured_theme(cx);
-        cx.update_global::<Self, _>(|this, _| this.theme = theme);
-        cx.refresh_windows();
-    }
-
-    fn configured_icon_theme(cx: &mut App) -> Arc<IconTheme> {
-        let themes = ThemeRegistry::default_global(cx);
-        let theme_settings = ThemeSettings::get_global(cx);
-        let system_appearance = SystemAppearance::global(cx);
-
-        let icon_theme_name = theme_settings.icon_theme.name(*system_appearance);
-
-        match themes.get_icon_theme(&icon_theme_name.0) {
-            Ok(theme) => theme,
-            Err(err) => {
-                if themes.extensions_loaded() {
-                    log::error!("{err}");
-                }
-                themes.get_icon_theme(DEFAULT_ICON_THEME_NAME).unwrap()
-            }
-        }
-    }
-
-    /// Reloads the current icon theme.
-    ///
-    /// Reads the [`ThemeSettings`] to know which icon theme should be loaded,
-    /// taking into account the current [`SystemAppearance`].
-    pub fn reload_icon_theme(cx: &mut App) {
-        let icon_theme = Self::configured_icon_theme(cx);
-        cx.update_global::<Self, _>(|this, _| this.icon_theme = icon_theme);
-        cx.refresh_windows();
-    }
-
-    /// the active theme
-    pub fn theme(cx: &App) -> &Arc<Theme> {
-        &cx.global::<Self>().theme
-    }
-
-    /// the active icon theme
-    pub fn icon_theme(cx: &App) -> &Arc<IconTheme> {
-        &cx.global::<Self>().icon_theme
-    }
-}

crates/theme_extension/src/theme_extension.rs 🔗

@@ -5,7 +5,7 @@ use anyhow::Result;
 use extension::{ExtensionHostProxy, ExtensionThemeProxy};
 use fs::Fs;
 use gpui::{App, BackgroundExecutor, SharedString, Task};
-use theme::{GlobalTheme, ThemeRegistry};
+use theme::{ThemeRegistry, ThemeSettings};
 
 pub fn init(
     extension_host_proxy: Arc<ExtensionHostProxy>,
@@ -46,7 +46,7 @@ impl ExtensionThemeProxy for ThemeRegistryProxy {
     }
 
     fn reload_current_theme(&self, cx: &mut App) {
-        GlobalTheme::reload_theme(cx)
+        ThemeSettings::reload_current_theme(cx)
     }
 
     fn list_icon_theme_names(
@@ -83,6 +83,6 @@ impl ExtensionThemeProxy for ThemeRegistryProxy {
     }
 
     fn reload_current_icon_theme(&self, cx: &mut App) {
-        GlobalTheme::reload_icon_theme(cx)
+        ThemeSettings::reload_current_icon_theme(cx)
     }
 }

crates/theme_selector/src/icon_theme_selector.rs 🔗

@@ -7,10 +7,7 @@ use gpui::{
 use picker::{Picker, PickerDelegate};
 use settings::{Settings as _, SettingsStore, update_settings_file};
 use std::sync::Arc;
-use theme::{
-    Appearance, IconThemeName, IconThemeSelection, SystemAppearance, ThemeMeta, ThemeRegistry,
-    ThemeSettings,
-};
+use theme::{Appearance, IconTheme, ThemeMeta, ThemeRegistry, ThemeSettings};
 use ui::{ListItem, ListItemSpacing, prelude::*, v_flex};
 use util::ResultExt;
 use workspace::{ModalView, ui::HighlightedLabel};
@@ -54,9 +51,9 @@ pub(crate) struct IconThemeSelectorDelegate {
     fs: Arc<dyn Fs>,
     themes: Vec<ThemeMeta>,
     matches: Vec<StringMatch>,
-    original_theme: IconThemeName,
+    original_theme: Arc<IconTheme>,
     selection_completed: bool,
-    selected_theme: Option<IconThemeName>,
+    selected_theme: Option<Arc<IconTheme>>,
     selected_index: usize,
     selector: WeakEntity<IconThemeSelector>,
 }
@@ -69,9 +66,7 @@ impl IconThemeSelectorDelegate {
         cx: &mut Context<IconThemeSelector>,
     ) -> Self {
         let theme_settings = ThemeSettings::get_global(cx);
-        let original_theme = theme_settings
-            .icon_theme
-            .name(SystemAppearance::global(cx).0);
+        let original_theme = theme_settings.active_icon_theme.clone();
 
         let registry = ThemeRegistry::global(cx);
         let mut themes = registry
@@ -112,18 +107,29 @@ impl IconThemeSelectorDelegate {
             selector,
         };
 
-        this.select_if_matching(&original_theme.0);
+        this.select_if_matching(&original_theme.name);
         this
     }
 
     fn show_selected_theme(
         &mut self,
         cx: &mut Context<Picker<IconThemeSelectorDelegate>>,
-    ) -> Option<IconThemeName> {
-        let mat = self.matches.get(self.selected_index)?;
-        let name = IconThemeName(mat.string.clone().into());
-        Self::set_icon_theme(name.clone(), cx);
-        Some(name)
+    ) -> Option<Arc<IconTheme>> {
+        if let Some(mat) = self.matches.get(self.selected_index) {
+            let registry = ThemeRegistry::global(cx);
+            match registry.get_icon_theme(&mat.string) {
+                Ok(theme) => {
+                    Self::set_icon_theme(theme.clone(), cx);
+                    Some(theme)
+                }
+                Err(err) => {
+                    log::error!("error loading icon theme {}: {err}", mat.string);
+                    None
+                }
+            }
+        } else {
+            None
+        }
     }
 
     fn select_if_matching(&mut self, theme_name: &str) {
@@ -134,11 +140,12 @@ impl IconThemeSelectorDelegate {
             .unwrap_or(self.selected_index);
     }
 
-    fn set_icon_theme(name: IconThemeName, cx: &mut App) {
-        SettingsStore::update_global(cx, |store, _| {
+    fn set_icon_theme(theme: Arc<IconTheme>, cx: &mut App) {
+        SettingsStore::update_global(cx, |store, cx| {
             let mut theme_settings = store.get::<ThemeSettings>(None).clone();
-            theme_settings.icon_theme = IconThemeSelection::Static(name);
+            theme_settings.active_icon_theme = theme;
             store.override_global(theme_settings);
+            cx.refresh_windows();
         });
     }
 }
@@ -163,9 +170,7 @@ impl PickerDelegate for IconThemeSelectorDelegate {
         self.selection_completed = true;
 
         let theme_settings = ThemeSettings::get_global(cx);
-        let theme_name = theme_settings
-            .icon_theme
-            .name(SystemAppearance::global(cx).0);
+        let theme_name = theme_settings.active_icon_theme.name.clone();
 
         telemetry::event!(
             "Settings Changed",
@@ -176,7 +181,7 @@ impl PickerDelegate for IconThemeSelectorDelegate {
         let appearance = Appearance::from(window.appearance());
 
         update_settings_file(self.fs.clone(), cx, move |settings, _| {
-            theme::set_icon_theme(settings, theme_name, appearance);
+            theme::set_icon_theme(settings, theme_name.to_string(), appearance);
         });
 
         self.selector
@@ -263,7 +268,7 @@ impl PickerDelegate for IconThemeSelectorDelegate {
                         .matches
                         .iter()
                         .enumerate()
-                        .find(|(_, mtch)| mtch.string.as_str() == selected.0.as_ref())
+                        .find(|(_, mtch)| mtch.string == selected.name)
                         .map(|(ix, _)| ix)
                         .unwrap_or_default();
                 } else {

crates/theme_selector/src/theme_selector.rs 🔗

@@ -203,11 +203,12 @@ impl ThemeSelectorDelegate {
     }
 
     fn set_theme(theme: Arc<Theme>, cx: &mut App) {
-        SettingsStore::update_global(cx, |store, _| {
+        SettingsStore::update_global(cx, |store, cx| {
             let mut theme_settings = store.get::<ThemeSettings>(None).clone();
-            let name = theme.as_ref().name.clone().into();
-            theme_settings.theme = theme::ThemeSelection::Static(theme::ThemeName(name));
+            theme_settings.active_theme = theme;
+            theme_settings.apply_theme_overrides();
             store.override_global(theme_settings);
+            cx.refresh_windows();
         });
     }
 }

crates/title_bar/src/title_bar_settings.rs 🔗

@@ -1,4 +1,5 @@
 use settings::{Settings, SettingsContent};
+use ui::App;
 
 #[derive(Copy, Clone, Debug)]
 pub struct TitleBarSettings {
@@ -12,7 +13,7 @@ pub struct TitleBarSettings {
 }
 
 impl Settings for TitleBarSettings {
-    fn from_settings(s: &SettingsContent) -> Self {
+    fn from_settings(s: &SettingsContent, _: &mut App) -> Self {
         let content = s.title_bar.clone().unwrap();
         TitleBarSettings {
             show_branch_icon: content.show_branch_icon.unwrap(),

crates/vim/src/vim.rs 🔗

@@ -1913,7 +1913,7 @@ impl From<settings::ModeContent> for Mode {
 }
 
 impl Settings for VimSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         let vim = content.vim.clone().unwrap();
         Self {
             default_mode: vim.default_mode.unwrap().into(),

crates/vim_mode_setting/src/vim_mode_setting.rs 🔗

@@ -16,7 +16,7 @@ pub fn init(cx: &mut App) {
 pub struct VimModeSetting(pub bool);
 
 impl Settings for VimModeSetting {
-    fn from_settings(content: &SettingsContent) -> Self {
+    fn from_settings(content: &SettingsContent, _cx: &mut App) -> Self {
         Self(content.vim_mode.unwrap())
     }
 
@@ -28,7 +28,7 @@ impl Settings for VimModeSetting {
 pub struct HelixModeSetting(pub bool);
 
 impl Settings for HelixModeSetting {
-    fn from_settings(content: &SettingsContent) -> Self {
+    fn from_settings(content: &SettingsContent, _cx: &mut App) -> Self {
         Self(content.helix_mode.unwrap())
     }
 

crates/workspace/src/item.rs 🔗

@@ -65,7 +65,7 @@ pub struct PreviewTabsSettings {
 }
 
 impl Settings for ItemSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         let tabs = content.tabs.as_ref().unwrap();
         Self {
             git_status: tabs.git_status.unwrap(),
@@ -113,7 +113,7 @@ impl Settings for ItemSettings {
 }
 
 impl Settings for PreviewTabsSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         let preview_tabs = content.preview_tabs.as_ref().unwrap();
         Self {
             enabled: preview_tabs.enabled.unwrap(),

crates/workspace/src/workspace.rs 🔗

@@ -103,7 +103,7 @@ use std::{
     time::Duration,
 };
 use task::{DebugScenario, SpawnInTerminal, TaskContext};
-use theme::{ActiveTheme, GlobalTheme, SystemAppearance, ThemeSettings};
+use theme::{ActiveTheme, SystemAppearance, ThemeSettings};
 pub use toolbar::{Toolbar, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
 pub use ui;
 use ui::{Window, prelude::*};
@@ -1435,8 +1435,8 @@ impl Workspace {
 
                 *SystemAppearance::global_mut(cx) = SystemAppearance(window_appearance.into());
 
-                GlobalTheme::reload_theme(cx);
-                GlobalTheme::reload_icon_theme(cx);
+                ThemeSettings::reload_current_theme(cx);
+                ThemeSettings::reload_current_icon_theme(cx);
             }),
             cx.on_release(move |this, cx| {
                 this.app_state.workspace_store.update(cx, move |store, _| {

crates/workspace/src/workspace_settings.rs 🔗

@@ -2,6 +2,7 @@ use std::num::NonZeroUsize;
 
 use crate::DockPosition;
 use collections::HashMap;
+use gpui::App;
 use serde::Deserialize;
 pub use settings::AutosaveSetting;
 use settings::Settings;
@@ -61,7 +62,7 @@ pub struct TabBarSettings {
 }
 
 impl Settings for WorkspaceSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         let workspace = &content.workspace;
         Self {
             active_pane_modifiers: ActivePanelModifiers {
@@ -196,7 +197,7 @@ impl Settings for WorkspaceSettings {
 }
 
 impl Settings for TabBarSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         let tab_bar = content.tab_bar.clone().unwrap();
         TabBarSettings {
             show: tab_bar.show.unwrap(),
@@ -230,7 +231,7 @@ pub struct StatusBarSettings {
 }
 
 impl Settings for StatusBarSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         let status_bar = content.status_bar.clone().unwrap();
         StatusBarSettings {
             show: status_bar.show.unwrap(),

crates/worktree/src/worktree_settings.rs 🔗

@@ -1,6 +1,7 @@
 use std::path::Path;
 
 use anyhow::Context as _;
+use gpui::App;
 use settings::{Settings, SettingsContent};
 use util::{
     ResultExt,
@@ -34,7 +35,7 @@ impl WorktreeSettings {
 }
 
 impl Settings for WorktreeSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
         let worktree = content.project.worktree.clone();
         let file_scan_exclusions = worktree.file_scan_exclusions.unwrap();
         let file_scan_inclusions = worktree.file_scan_inclusions.unwrap();

crates/zed/src/main.rs 🔗

@@ -41,8 +41,8 @@ use std::{
     sync::Arc,
 };
 use theme::{
-    ActiveTheme, GlobalTheme, IconThemeNotFoundError, SystemAppearance, ThemeNotFoundError,
-    ThemeRegistry, ThemeSettings,
+    ActiveTheme, IconThemeNotFoundError, SystemAppearance, ThemeNotFoundError, ThemeRegistry,
+    ThemeSettings,
 };
 use util::{ResultExt, TryFutureExt, maybe};
 use uuid::Uuid;
@@ -542,6 +542,7 @@ pub fn main() {
             cx,
         );
 
+        SystemAppearance::init(cx);
         theme::init(theme::LoadThemes::All(Box::new(Assets)), cx);
         theme_extension::init(
             extension_host_proxy.clone(),
@@ -1362,49 +1363,49 @@ fn eager_load_active_theme_and_icon_theme(fs: Arc<dyn Fs>, cx: &App) {
     let theme_settings = ThemeSettings::get_global(cx);
     let appearance = SystemAppearance::global(cx).0;
 
-    let theme_name = theme_settings.theme.name(appearance);
-    if matches!(
-        theme_registry.get(&theme_name.0),
-        Err(ThemeNotFoundError(_))
-    ) && let Some(theme_path) = extension_store
-        .read(cx)
-        .path_to_extension_theme(&theme_name.0)
-    {
-        cx.spawn({
-            let theme_registry = theme_registry.clone();
-            let fs = fs.clone();
-            async move |cx| {
-                theme_registry.load_user_theme(&theme_path, fs).await?;
+    if let Some(theme_selection) = theme_settings.theme_selection.as_ref() {
+        let theme_name = theme_selection.theme(appearance);
+        if matches!(theme_registry.get(theme_name), Err(ThemeNotFoundError(_)))
+            && let Some(theme_path) = extension_store.read(cx).path_to_extension_theme(theme_name)
+        {
+            cx.spawn({
+                let theme_registry = theme_registry.clone();
+                let fs = fs.clone();
+                async move |cx| {
+                    theme_registry.load_user_theme(&theme_path, fs).await?;
 
-                cx.update(|cx| {
-                    GlobalTheme::reload_theme(cx);
-                })
-            }
-        })
-        .detach_and_log_err(cx);
+                    cx.update(|cx| {
+                        ThemeSettings::reload_current_theme(cx);
+                    })
+                }
+            })
+            .detach_and_log_err(cx);
+        }
     }
 
-    let icon_theme_name = theme_settings.icon_theme.name(appearance);
-    if matches!(
-        theme_registry.get_icon_theme(&icon_theme_name.0),
-        Err(IconThemeNotFoundError(_))
-    ) && let Some((icon_theme_path, icons_root_path)) = extension_store
-        .read(cx)
-        .path_to_extension_icon_theme(&icon_theme_name.0)
-    {
-        cx.spawn({
-            let fs = fs.clone();
-            async move |cx| {
-                theme_registry
-                    .load_icon_theme(&icon_theme_path, &icons_root_path, fs)
-                    .await?;
+    if let Some(icon_theme_selection) = theme_settings.icon_theme_selection.as_ref() {
+        let icon_theme_name = icon_theme_selection.icon_theme(appearance);
+        if matches!(
+            theme_registry.get_icon_theme(icon_theme_name),
+            Err(IconThemeNotFoundError(_))
+        ) && let Some((icon_theme_path, icons_root_path)) = extension_store
+            .read(cx)
+            .path_to_extension_icon_theme(icon_theme_name)
+        {
+            cx.spawn({
+                let fs = fs.clone();
+                async move |cx| {
+                    theme_registry
+                        .load_icon_theme(&icon_theme_path, &icons_root_path, fs)
+                        .await?;
 
-                cx.update(|cx| {
-                    GlobalTheme::reload_icon_theme(cx);
-                })
-            }
-        })
-        .detach_and_log_err(cx);
+                    cx.update(|cx| {
+                        ThemeSettings::reload_current_icon_theme(cx);
+                    })
+                }
+            })
+            .detach_and_log_err(cx);
+        }
     }
 }
 
@@ -1432,7 +1433,7 @@ fn load_user_themes_in_background(fs: Arc<dyn fs::Fs>, cx: &mut App) {
                     }
                 }
                 theme_registry.load_user_themes(themes_dir, fs).await?;
-                cx.update(GlobalTheme::reload_theme)?;
+                cx.update(ThemeSettings::reload_current_theme)?;
             }
             anyhow::Ok(())
         }
@@ -1458,7 +1459,7 @@ fn watch_themes(fs: Arc<dyn fs::Fs>, cx: &mut App) {
                         .await
                         .log_err()
                 {
-                    cx.update(GlobalTheme::reload_theme).log_err();
+                    cx.update(ThemeSettings::reload_current_theme).log_err();
                 }
             }
         }

crates/zed/src/zed.rs 🔗

@@ -1997,11 +1997,8 @@ mod tests {
         path::{Path, PathBuf},
         time::Duration,
     };
-    use theme::ThemeRegistry;
-    use util::{
-        path,
-        rel_path::{RelPath, rel_path},
-    };
+    use theme::{ThemeRegistry, ThemeSettings};
+    use util::{path, rel_path::rel_path};
     use workspace::{
         NewFile, OpenOptions, OpenVisible, SERIALIZATION_THROTTLE_TIME, SaveIntent, SplitDirection,
         WorkspaceHandle,
@@ -4599,7 +4596,7 @@ mod tests {
         for theme_name in themes.list().into_iter().map(|meta| meta.name) {
             let theme = themes.get(&theme_name).unwrap();
             assert_eq!(theme.name, theme_name);
-            if theme.name.as_ref() == "One Dark" {
+            if theme.name == ThemeSettings::get(None, cx).active_theme.name {
                 has_default_theme = true;
             }
         }

crates/zlog_settings/src/zlog_settings.rs 🔗

@@ -24,7 +24,7 @@ pub struct ZlogSettings {
 }
 
 impl Settings for ZlogSettings {
-    fn from_settings(content: &settings::SettingsContent) -> Self {
+    fn from_settings(content: &settings::SettingsContent, _: &mut App) -> Self {
         ZlogSettings {
             scopes: content.log.clone().unwrap(),
         }