diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 1209b8dfb7..60f781973b 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -10809,15 +10809,26 @@ impl Editor { pub fn register_action( &mut self, listener: impl Fn(&A, &mut Window, &mut App) + 'static, + ) -> Subscription { + self.register_action_erased( + TypeId::of::(), + Arc::new(move |action, window, cx| { + listener(action.downcast_ref().unwrap(), window, cx) + }), + ) + } + + fn register_action_erased( + &mut self, + action_type: TypeId, + listener: Arc, ) -> Subscription { let id = self.next_editor_action_id.post_inc(); - let listener = Arc::new(listener); self.editor_actions.borrow_mut().insert( id, Box::new(move |_, window, _| { let listener = listener.clone(); - window.on_action(TypeId::of::(), move |action, phase, window, cx| { - let action = action.downcast_ref().unwrap(); + window.on_action(action_type, move |action, phase, window, cx| { if phase == DispatchPhase::Bubble { listener(action, window, cx) } diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index da35ae9644..918d2214da 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -10678,10 +10678,25 @@ pub fn register_action( editor: &Entity, window: &mut Window, listener: impl Fn(&mut Editor, &T, &mut Window, &mut Context) + 'static, +) { + register_action_erased( + editor, + window, + TypeId::of::(), + Box::new(move |editor, action, window, cx| { + listener(editor, action.downcast_ref().unwrap(), window, cx) + }), + ) +} + +fn register_action_erased( + editor: &Entity, + window: &mut Window, + action_type: TypeId, + listener: Box)>, ) { let editor = editor.clone(); - window.on_action(TypeId::of::(), move |action, phase, window, cx| { - let action = action.downcast_ref().unwrap(); + window.on_action(action_type, move |action, phase, window, cx| { if phase == DispatchPhase::Bubble { editor.update(cx, |editor, cx| { listener(editor, action, window, cx); diff --git a/crates/settings/src/settings_store.rs b/crates/settings/src/settings_store.rs index 8ef05e6fef..6590d6be71 100644 --- a/crates/settings/src/settings_store.rs +++ b/crates/settings/src/settings_store.rs @@ -559,7 +559,7 @@ impl SettingsStore { fn update_settings_file_inner( &self, fs: Arc, - update: impl 'static + Send + FnOnce(String, AsyncApp) -> Result, + update: Box Result>, ) -> oneshot::Receiver> { let (tx, rx) = oneshot::channel::>(); self.setting_file_updates_tx @@ -626,11 +626,17 @@ impl SettingsStore { fs: Arc, update: impl 'static + Send + FnOnce(&mut SettingsContent, &App), ) -> oneshot::Receiver> { - self.update_settings_file_inner(fs, move |old_text: String, cx: AsyncApp| { - cx.read_global(|store: &SettingsStore, cx| { - store.new_text_for_update(old_text, |content| update(content, cx)) - }) - }) + let mut update = Some(update); + self.update_settings_file_inner( + fs, + Box::new(move |old_text: String, cx: AsyncApp| { + cx.read_global(|store: &SettingsStore, cx| { + store.new_text_for_update_inner(old_text, &mut |content| { + (update.take().expect("called once"))(content, cx) + }) + }) + }), + ) } pub fn import_vscode_settings( @@ -638,11 +644,14 @@ impl SettingsStore { fs: Arc, vscode_settings: VsCodeSettings, ) -> oneshot::Receiver> { - self.update_settings_file_inner(fs, move |old_text: String, cx: AsyncApp| { - cx.read_global(|store: &SettingsStore, _cx| { - store.get_vscode_edits(old_text, &vscode_settings) - }) - }) + self.update_settings_file_inner( + fs, + Box::new(move |old_text: String, cx: AsyncApp| { + cx.read_global(|store: &SettingsStore, _cx| { + store.get_vscode_edits(old_text, &vscode_settings) + }) + }), + ) } pub fn get_all_files(&self) -> Vec { @@ -833,7 +842,18 @@ impl SettingsStore { old_text: String, update: impl FnOnce(&mut SettingsContent), ) -> Result { - let edits = self.edits_for_update(&old_text, update)?; + let mut update = Some(update); + self.new_text_for_update_inner(old_text, &mut |content| { + (update.take().expect("called once"))(content) + }) + } + + fn new_text_for_update_inner( + &self, + old_text: String, + update: &mut dyn FnMut(&mut SettingsContent), + ) -> Result { + let edits = self.edits_for_update_inner(&old_text, update)?; let mut new_text = old_text; for (range, replacement) in edits.into_iter() { new_text.replace_range(range, &replacement); @@ -853,6 +873,17 @@ impl SettingsStore { &self, text: &str, update: impl FnOnce(&mut SettingsContent), + ) -> Result, String)>> { + let mut update = Some(update); + self.edits_for_update_inner(text, &mut |content| { + (update.take().expect("called once"))(content) + }) + } + + fn edits_for_update_inner( + &self, + text: &str, + update: &mut dyn FnMut(&mut SettingsContent), ) -> Result, String)>> { let old_content = if text.trim().is_empty() { UserSettingsContent::default() diff --git a/crates/settings_ui/src/components/dropdown.rs b/crates/settings_ui/src/components/dropdown.rs index c3189129e9..04ab548f35 100644 --- a/crates/settings_ui/src/components/dropdown.rs +++ b/crates/settings_ui/src/components/dropdown.rs @@ -8,37 +8,47 @@ use ui::{ }; #[derive(IntoElement)] -pub struct EnumVariantDropdown -where - T: strum::VariantArray + strum::VariantNames + Copy + PartialEq + Send + Sync + 'static, -{ +pub struct EnumVariantDropdown { id: ElementId, - current_value: T, - variants: &'static [T], + selected_index: usize, labels: &'static [&'static str], should_do_title_case: bool, tab_index: Option, disabled: bool, aria_label: Option, aria_description: Option, - on_change: Rc, + on_change: Rc, } -impl EnumVariantDropdown -where - T: strum::VariantArray + strum::VariantNames + Copy + PartialEq + Send + Sync + 'static, -{ - pub fn new( +impl EnumVariantDropdown { + pub fn new( id: impl Into, current_value: T, variants: &'static [T], labels: &'static [&'static str], on_change: impl Fn(T, &mut ui::Window, &mut App) + 'static, + ) -> Self + where + T: strum::VariantArray + strum::VariantNames + Copy + PartialEq + Send + Sync + 'static, + { + let selected_index = variants + .iter() + .position(|v| *v == current_value) + .unwrap_or(0); + Self::new_indexed(id, selected_index, labels, move |index, window, cx| { + on_change(variants[index], window, cx) + }) + } + + pub fn new_indexed( + id: impl Into, + selected_index: usize, + labels: &'static [&'static str], + on_change: impl Fn(usize, &mut ui::Window, &mut App) + 'static, ) -> Self { Self { id: id.into(), - current_value, - variants, + selected_index, labels, should_do_title_case: true, tab_index: None, @@ -79,33 +89,25 @@ where } } -impl RenderOnce for EnumVariantDropdown -where - T: strum::VariantArray + strum::VariantNames + Copy + PartialEq + Send + Sync + 'static, -{ +impl RenderOnce for EnumVariantDropdown { fn render(self, window: &mut ui::Window, cx: &mut ui::App) -> impl gpui::IntoElement { - let current_value_label = self.labels[self - .variants - .iter() - .position(|v| *v == self.current_value) - .unwrap()]; + let current_value_label = self.labels[self.selected_index]; let context_menu = window.use_keyed_state(current_value_label, cx, |window, cx| { ContextMenu::new(window, cx, move |mut menu, _, _| { - for (&value, &label) in std::iter::zip(self.variants, self.labels) { + for (index, &label) in self.labels.iter().enumerate() { let on_change = self.on_change.clone(); - let current_value = self.current_value; menu = menu.toggleable_entry( if self.should_do_title_case { label.to_title_case() } else { label.to_string() }, - value == current_value, + index == self.selected_index, IconPosition::End, None, move |window, cx| { - on_change(value, window, cx); + on_change(index, window, cx); }, ); } diff --git a/crates/settings_ui/src/settings_ui.rs b/crates/settings_ui/src/settings_ui.rs index f62905e8c7..eee5c6e3ec 100644 --- a/crates/settings_ui/src/settings_ui.rs +++ b/crates/settings_ui/src/settings_ui.rs @@ -354,33 +354,52 @@ impl SettingFieldRenderer { ) -> Stateful
+ 'static, ) -> &mut Self { - let key = TypeId::of::(); - let renderer = Box::new( - move |settings_window: &SettingsWindow, - item: &SettingItem, - settings_file: SettingsUiFile, - metadata: Option<&SettingsFieldMetadata>, - sub_field: bool, - window: &mut Window, - cx: &mut Context| { - let field = *item - .field - .as_ref() - .as_any() - .downcast_ref::>() - .unwrap(); - renderer( - settings_window, - item, - field, - settings_file, - metadata, - sub_field, - window, - cx, - ) - }, - ); + self.add_renderer_erased( + TypeId::of::(), + Box::new( + move |settings_window: &SettingsWindow, + item: &SettingItem, + settings_file: SettingsUiFile, + metadata: Option<&SettingsFieldMetadata>, + sub_field: bool, + window: &mut Window, + cx: &mut Context| { + let field = *item + .field + .as_ref() + .as_any() + .downcast_ref::>() + .unwrap(); + renderer( + settings_window, + item, + field, + settings_file, + metadata, + sub_field, + window, + cx, + ) + }, + ), + ) + } + + fn add_renderer_erased( + &mut self, + key: TypeId, + renderer: Box< + dyn Fn( + &SettingsWindow, + &SettingItem, + SettingsUiFile, + Option<&SettingsFieldMetadata>, + bool, + &mut Window, + &mut Context, + ) -> Stateful
, + >, + ) -> &mut Self { self.renderers.borrow_mut().insert(key, renderer); self }