Detailed changes
@@ -3528,6 +3528,7 @@ dependencies = [
"settings",
"smallvec",
"theme",
+ "theme_selector",
"ui",
"util",
"workspace",
@@ -9619,6 +9620,7 @@ dependencies = [
"gpui",
"log",
"picker",
+ "serde",
"settings",
"theme",
"ui",
@@ -689,7 +689,7 @@ impl CollabTitlebarItem {
ContextMenu::build(cx, |menu, _| {
menu.action("Settings", zed_actions::OpenSettings.boxed_clone())
.action("Extensions", extensions_ui::Extensions.boxed_clone())
- .action("Themes...", theme_selector::Toggle.boxed_clone())
+ .action("Themes...", theme_selector::Toggle::default().boxed_clone())
.separator()
.action("Sign Out", client::SignOut.boxed_clone())
})
@@ -713,7 +713,7 @@ impl CollabTitlebarItem {
ContextMenu::build(cx, |menu, _| {
menu.action("Settings", zed_actions::OpenSettings.boxed_clone())
.action("Extensions", extensions_ui::Extensions.boxed_clone())
- .action("Themes...", theme_selector::Toggle.boxed_clone())
+ .action("Themes...", theme_selector::Toggle::default().boxed_clone())
})
.into()
})
@@ -92,16 +92,18 @@ pub enum ExtensionStatus {
Removing,
}
+#[derive(Clone, Copy)]
enum ExtensionOperation {
Upgrade,
Install,
Remove,
}
-#[derive(Copy, Clone)]
+#[derive(Clone)]
pub enum Event {
ExtensionsUpdated,
StartedReloading,
+ ExtensionInstalled(Arc<str>),
}
impl EventEmitter<Event> for ExtensionStore {}
@@ -330,6 +332,7 @@ impl ExtensionStore {
.unbounded_send(modified_extension)
.expect("reload task exited");
cx.emit(Event::StartedReloading);
+
async move {
rx.await.ok();
}
@@ -358,6 +361,17 @@ impl ExtensionStore {
.filter_map(|extension| extension.dev.then_some(&extension.manifest))
}
+ /// Returns the names of themes provided by extensions.
+ pub fn extension_themes<'a>(
+ &'a self,
+ extension_id: &'a str,
+ ) -> impl Iterator<Item = &'a Arc<str>> {
+ self.extension_index
+ .themes
+ .iter()
+ .filter_map(|(name, theme)| theme.extension.as_ref().eq(extension_id).then_some(name))
+ }
+
pub fn fetch_extensions(
&self,
search: Option<&str>,
@@ -441,8 +455,21 @@ impl ExtensionStore {
archive
.unpack(extensions_dir.join(extension_id.as_ref()))
.await?;
- this.update(&mut cx, |this, cx| this.reload(Some(extension_id), cx))?
- .await;
+ this.update(&mut cx, |this, cx| {
+ this.reload(Some(extension_id.clone()), cx)
+ })?
+ .await;
+
+ match operation {
+ ExtensionOperation::Install => {
+ this.update(&mut cx, |_, cx| {
+ cx.emit(Event::ExtensionInstalled(extension_id));
+ })
+ .ok();
+ }
+ _ => {}
+ }
+
anyhow::Ok(())
})
.detach_and_log_err(cx);
@@ -28,6 +28,7 @@ serde.workspace = true
settings.workspace = true
smallvec.workspace = true
theme.workspace = true
+theme_selector.workspace = true
ui.workspace = true
util.workspace = true
workspace.workspace = true
@@ -9,7 +9,7 @@ use fuzzy::{match_strings, StringMatchCandidate};
use gpui::{
actions, canvas, uniform_list, AnyElement, AppContext, EventEmitter, FocusableView, FontStyle,
FontWeight, InteractiveElement, KeyContext, ParentElement, Render, Styled, Task, TextStyle,
- UniformListScrollHandle, View, ViewContext, VisualContext, WhiteSpace, WindowContext,
+ UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WhiteSpace, WindowContext,
};
use settings::Settings;
use std::ops::DerefMut;
@@ -100,10 +100,14 @@ impl ExtensionsPage {
pub fn new(workspace: &Workspace, cx: &mut ViewContext<Workspace>) -> View<Self> {
cx.new_view(|cx: &mut ViewContext<Self>| {
let store = ExtensionStore::global(cx);
+ let workspace_handle = workspace.weak_handle();
let subscriptions = [
cx.observe(&store, |_, _, cx| cx.notify()),
- cx.subscribe(&store, |this, _, event, cx| match event {
+ cx.subscribe(&store, move |this, _, event, cx| match event {
extension::Event::ExtensionsUpdated => this.fetch_extensions_debounced(cx),
+ extension::Event::ExtensionInstalled(extension_id) => {
+ this.on_extension_installed(workspace_handle.clone(), extension_id, cx)
+ }
_ => {}
}),
];
@@ -133,6 +137,32 @@ impl ExtensionsPage {
})
}
+ fn on_extension_installed(
+ &mut self,
+ workspace: WeakView<Workspace>,
+ extension_id: &str,
+ cx: &mut ViewContext<Self>,
+ ) {
+ let extension_store = ExtensionStore::global(cx).read(cx);
+ let themes = extension_store
+ .extension_themes(extension_id)
+ .map(|name| name.to_string())
+ .collect::<Vec<_>>();
+ if !themes.is_empty() {
+ workspace
+ .update(cx, |workspace, cx| {
+ theme_selector::toggle(
+ workspace,
+ &theme_selector::Toggle {
+ themes_filter: Some(themes),
+ },
+ cx,
+ )
+ })
+ .ok();
+ }
+ }
+
fn filter_extension_entries(&mut self, cx: &mut ViewContext<Self>) {
let extension_store = ExtensionStore::global(cx).read(cx);
@@ -20,6 +20,7 @@ fuzzy.workspace = true
gpui.workspace = true
log.workspace = true
picker.workspace = true
+serde.workspace = true
settings.workspace = true
theme.workspace = true
ui.workspace = true
@@ -3,10 +3,11 @@ use feature_flags::FeatureFlagAppExt;
use fs::Fs;
use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
use gpui::{
- actions, AppContext, DismissEvent, EventEmitter, FocusableView, Render, View, ViewContext,
- VisualContext, WeakView,
+ actions, impl_actions, AppContext, DismissEvent, EventEmitter, FocusableView, Render, View,
+ ViewContext, VisualContext, WeakView,
};
use picker::{Picker, PickerDelegate};
+use serde::Deserialize;
use settings::{update_settings_file, SettingsStore};
use std::sync::Arc;
use theme::{
@@ -16,7 +17,14 @@ use ui::{prelude::*, v_flex, ListItem, ListItemSpacing};
use util::ResultExt;
use workspace::{ui::HighlightedLabel, ModalView, Workspace};
-actions!(theme_selector, [Toggle, Reload]);
+#[derive(PartialEq, Clone, Default, Debug, Deserialize)]
+pub struct Toggle {
+ /// A list of theme names to filter the theme selector down to.
+ pub themes_filter: Option<Vec<String>>,
+}
+
+impl_actions!(theme_selector, [Toggle]);
+actions!(theme_selector, [Reload]);
pub fn init(cx: &mut AppContext) {
cx.observe_new_views(
@@ -27,14 +35,18 @@ pub fn init(cx: &mut AppContext) {
.detach();
}
-pub fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
+pub fn toggle(workspace: &mut Workspace, toggle: &Toggle, cx: &mut ViewContext<Workspace>) {
let fs = workspace.app_state().fs.clone();
let telemetry = workspace.client().telemetry().clone();
workspace.toggle_modal(cx, |cx| {
- ThemeSelector::new(
- ThemeSelectorDelegate::new(cx.view().downgrade(), fs, telemetry, cx),
+ let delegate = ThemeSelectorDelegate::new(
+ cx.view().downgrade(),
+ fs,
+ telemetry,
+ toggle.themes_filter.as_ref(),
cx,
- )
+ );
+ ThemeSelector::new(delegate, cx)
});
}
@@ -81,13 +93,25 @@ impl ThemeSelectorDelegate {
weak_view: WeakView<ThemeSelector>,
fs: Arc<dyn Fs>,
telemetry: Arc<Telemetry>,
+ themes_filter: Option<&Vec<String>>,
cx: &mut ViewContext<ThemeSelector>,
) -> Self {
let original_theme = cx.theme().clone();
let staff_mode = cx.is_staff();
let registry = ThemeRegistry::global(cx);
- let mut themes = registry.list(staff_mode);
+ let mut themes = registry
+ .list(staff_mode)
+ .into_iter()
+ .filter(|meta| {
+ if let Some(theme_filter) = themes_filter {
+ theme_filter.contains(&meta.name.to_string())
+ } else {
+ true
+ }
+ })
+ .collect::<Vec<_>>();
+
themes.sort_unstable_by(|a, b| {
a.appearance
.is_light()
@@ -113,6 +137,7 @@ impl ThemeSelectorDelegate {
telemetry,
view: weak_view,
};
+
this.select_if_matching(&original_theme.name);
this
}
@@ -20,7 +20,7 @@ pub fn app_menus() -> Vec<Menu<'static>> {
MenuItem::action("Open Default Settings", super::OpenDefaultSettings),
MenuItem::action("Open Default Key Bindings", super::OpenDefaultKeymap),
MenuItem::action("Open Local Settings", super::OpenLocalSettings),
- MenuItem::action("Select Theme...", theme_selector::Toggle),
+ MenuItem::action("Select Theme...", theme_selector::Toggle::default()),
],
}),
MenuItem::action("Extensions", extensions_ui::Extensions),