file_icons: Fall back to the default icon theme for icons (#23196)

Marshall Bowers created

This PR updates the various `FileIcons` methods to fall back to the
default icon theme if the active icon theme does not have the desired
icon.

Release Notes:

- N/A

Change summary

crates/file_icons/src/file_icons.rs | 57 ++++++++++++++++++++----------
crates/theme/src/registry.rs        |  7 +++
2 files changed, 44 insertions(+), 20 deletions(-)

Detailed changes

crates/file_icons/src/file_icons.rs 🔗

@@ -1,3 +1,4 @@
+use std::sync::Arc;
 use std::{path::Path, str};
 
 use collections::HashMap;
@@ -5,7 +6,7 @@ use collections::HashMap;
 use gpui::{AppContext, AssetSource, Global, SharedString};
 use serde_derive::Deserialize;
 use settings::Settings;
-use theme::ThemeSettings;
+use theme::{IconTheme, ThemeRegistry, ThemeSettings};
 use util::{maybe, paths::PathExt};
 
 #[derive(Deserialize, Debug)]
@@ -58,33 +59,51 @@ impl FileIcons {
         .or_else(|| this.get_icon_for_type("default", cx))
     }
 
+    fn default_icon_theme(cx: &AppContext) -> Option<Arc<IconTheme>> {
+        let theme_registry = ThemeRegistry::global(cx);
+        theme_registry.default_icon_theme().ok()
+    }
+
     pub fn get_icon_for_type(&self, typ: &str, cx: &AppContext) -> Option<SharedString> {
-        let theme_settings = ThemeSettings::get_global(cx);
+        fn get_icon_for_type(icon_theme: &Arc<IconTheme>, typ: &str) -> Option<SharedString> {
+            icon_theme
+                .file_icons
+                .get(typ)
+                .map(|icon_definition| icon_definition.path.clone())
+        }
 
-        theme_settings
-            .active_icon_theme
-            .file_icons
-            .get(typ)
-            .map(|icon_definition| icon_definition.path.clone())
+        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))
+        })
     }
 
     pub fn get_folder_icon(expanded: bool, cx: &AppContext) -> Option<SharedString> {
-        let icon_theme = &ThemeSettings::get_global(cx).active_icon_theme;
-
-        if expanded {
-            icon_theme.directory_icons.expanded.clone()
-        } else {
-            icon_theme.directory_icons.collapsed.clone()
+        fn get_folder_icon(icon_theme: &Arc<IconTheme>, expanded: bool) -> Option<SharedString> {
+            if expanded {
+                icon_theme.directory_icons.expanded.clone()
+            } else {
+                icon_theme.directory_icons.collapsed.clone()
+            }
         }
+
+        get_folder_icon(&ThemeSettings::get_global(cx).active_icon_theme, expanded).or_else(|| {
+            Self::default_icon_theme(cx)
+                .and_then(|icon_theme| get_folder_icon(&icon_theme, expanded))
+        })
     }
 
     pub fn get_chevron_icon(expanded: bool, cx: &AppContext) -> Option<SharedString> {
-        let icon_theme = &ThemeSettings::get_global(cx).active_icon_theme;
-
-        if expanded {
-            icon_theme.chevron_icons.expanded.clone()
-        } else {
-            icon_theme.chevron_icons.collapsed.clone()
+        fn get_chevron_icon(icon_theme: &Arc<IconTheme>, expanded: bool) -> Option<SharedString> {
+            if expanded {
+                icon_theme.chevron_icons.expanded.clone()
+            } else {
+                icon_theme.chevron_icons.collapsed.clone()
+            }
         }
+
+        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/theme/src/registry.rs 🔗

@@ -12,7 +12,7 @@ use util::ResultExt;
 
 use crate::{
     read_user_theme, refine_theme_family, Appearance, IconTheme, Theme, ThemeFamily,
-    ThemeFamilyContent,
+    ThemeFamilyContent, DEFAULT_ICON_THEME_ID,
 };
 
 /// The metadata for a theme.
@@ -206,6 +206,11 @@ impl ThemeRegistry {
         Ok(())
     }
 
+    /// Returns the default icon theme.
+    pub fn default_icon_theme(&self) -> Result<Arc<IconTheme>> {
+        self.get_icon_theme(DEFAULT_ICON_THEME_ID)
+    }
+
     /// Returns the icon theme with the specified name.
     pub fn get_icon_theme(&self, name: &str) -> Result<Arc<IconTheme>> {
         self.state