diff --git a/crates/keymap_editor/src/keymap_editor.rs b/crates/keymap_editor/src/keymap_editor.rs index e81b1077c70d4eb3828715a6bcd28dfe564ab188..9e243d32151e3caeec2b8c51c7889d2ebe93f29b 100644 --- a/crates/keymap_editor/src/keymap_editor.rs +++ b/crates/keymap_editor/src/keymap_editor.rs @@ -81,50 +81,61 @@ pub fn init(cx: &mut App) { let keymap_event_channel = KeymapEventChannel::new(); cx.set_global(keymap_event_channel); - fn common(filter: Option, cx: &mut App) { - workspace::with_active_or_new_workspace(cx, move |workspace, window, cx| { - workspace - .with_local_workspace(window, cx, move |workspace, window, cx| { - let existing = workspace - .active_pane() - .read(cx) - .items() - .find_map(|item| item.downcast::()); - - let keymap_editor = if let Some(existing) = existing { - workspace.activate_item(&existing, true, true, window, cx); - existing - } else { - let keymap_editor = - cx.new(|cx| KeymapEditor::new(workspace.weak_handle(), window, cx)); - workspace.add_item_to_active_pane( - Box::new(keymap_editor.clone()), - None, - true, - window, - cx, - ); - keymap_editor - }; - - if let Some(filter) = filter { - keymap_editor.update(cx, |editor, cx| { - editor.filter_editor.update(cx, |editor, cx| { - editor.clear(window, cx); - editor.insert(&filter, window, cx); - }); - if !editor.has_binding_for(&filter) { - open_binding_modal_after_loading(cx) - } - }) - } - }) - .detach(); - }) + fn open_keymap_editor( + filter: Option, + workspace: &mut Workspace, + window: &mut Window, + cx: &mut Context, + ) { + workspace + .with_local_workspace(window, cx, |workspace, window, cx| { + let existing = workspace + .active_pane() + .read(cx) + .items() + .find_map(|item| item.downcast::()); + + let keymap_editor = if let Some(existing) = existing { + workspace.activate_item(&existing, true, true, window, cx); + existing + } else { + let keymap_editor = + cx.new(|cx| KeymapEditor::new(workspace.weak_handle(), window, cx)); + workspace.add_item_to_active_pane( + Box::new(keymap_editor.clone()), + None, + true, + window, + cx, + ); + keymap_editor + }; + + if let Some(filter) = filter { + keymap_editor.update(cx, |editor, cx| { + editor.filter_editor.update(cx, |editor, cx| { + editor.clear(window, cx); + editor.insert(&filter, window, cx); + }); + if !editor.has_binding_for(&filter) { + open_binding_modal_after_loading(cx) + } + }) + } + }) + .detach_and_log_err(cx); } - cx.on_action(|_: &OpenKeymap, cx| common(None, cx)) - .on_action(|action: &ChangeKeybinding, cx| common(Some(action.action.clone()), cx)); + cx.observe_new(|workspace: &mut Workspace, _window, _cx| { + workspace + .register_action(|workspace, _: &OpenKeymap, window, cx| { + open_keymap_editor(None, workspace, window, cx); + }) + .register_action(|workspace, action: &ChangeKeybinding, window, cx| { + open_keymap_editor(Some(action.action.clone()), workspace, window, cx); + }); + }) + .detach(); register_serializable_item::(cx); } diff --git a/crates/settings_ui/src/page_data.rs b/crates/settings_ui/src/page_data.rs index 79fc1cc11158399265a184a289fd8d7a71ce8d69..007c41ad59b4e875770beecb089bd4e7fb2078b5 100644 --- a/crates/settings_ui/src/page_data.rs +++ b/crates/settings_ui/src/page_data.rs @@ -1,12 +1,12 @@ -use gpui::App; +use gpui::{Action as _, App}; use settings::{LanguageSettingsContent, SettingsContent}; use std::sync::Arc; use strum::IntoDiscriminant as _; use ui::{IntoElement, SharedString}; use crate::{ - DynamicItem, PROJECT, SettingField, SettingItem, SettingsFieldMetadata, SettingsPage, - SettingsPageItem, SubPageLink, USER, all_language_names, sub_page_stack, + ActionLink, DynamicItem, PROJECT, SettingField, SettingItem, SettingsFieldMetadata, + SettingsPage, SettingsPageItem, SubPageLink, USER, all_language_names, sub_page_stack, }; const DEFAULT_STRING: String = String::new(); @@ -1054,6 +1054,25 @@ pub(crate) fn settings_data(cx: &App) -> Vec { SettingsPage { title: "Keymap", items: vec![ + SettingsPageItem::SectionHeader("Keybindings"), + SettingsPageItem::ActionLink(ActionLink { + title: "Edit Keybindings".into(), + description: Some("Customize keybindings in the keymap editor.".into()), + button_text: "Open Keymap".into(), + on_click: Arc::new(|settings_window, window, cx| { + let Some(original_window) = settings_window.original_window else { + return; + }; + original_window + .update(cx, |_workspace, original_window, cx| { + original_window + .dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx); + original_window.activate_window(); + }) + .ok(); + window.remove_window(); + }), + }), SettingsPageItem::SectionHeader("Base Keymap"), SettingsPageItem::SettingItem(SettingItem { title: "Base Keymap", diff --git a/crates/settings_ui/src/settings_ui.rs b/crates/settings_ui/src/settings_ui.rs index bfc60cc1ea21525effa5347431d90ee219064d24..40678f6cf8d1c6773ccf1168e065cb318ae9f14f 100644 --- a/crates/settings_ui/src/settings_ui.rs +++ b/crates/settings_ui/src/settings_ui.rs @@ -731,6 +731,7 @@ enum SettingsPageItem { SettingItem(SettingItem), SubPageLink(SubPageLink), DynamicItem(DynamicItem), + ActionLink(ActionLink), } impl std::fmt::Debug for SettingsPageItem { @@ -746,6 +747,9 @@ impl std::fmt::Debug for SettingsPageItem { SettingsPageItem::DynamicItem(dynamic_item) => { write!(f, "DynamicItem({})", dynamic_item.discriminant.title) } + SettingsPageItem::ActionLink(action_link) => { + write!(f, "ActionLink({})", action_link.title) + } } } } @@ -973,6 +977,55 @@ impl SettingsPageItem { return content.into_any_element(); } + SettingsPageItem::ActionLink(action_link) => v_flex() + .group("setting-item") + .px_8() + .child( + h_flex() + .id(action_link.title.clone()) + .w_full() + .min_w_0() + .justify_between() + .map(apply_padding) + .child( + v_flex() + .relative() + .w_full() + .max_w_1_2() + .child(Label::new(action_link.title.clone())) + .when_some( + action_link.description.as_ref(), + |this, description| { + this.child( + Label::new(description.clone()) + .size(LabelSize::Small) + .color(Color::Muted), + ) + }, + ), + ) + .child( + Button::new( + ("action-link".into(), action_link.title.clone()), + action_link.button_text.clone(), + ) + .icon(IconName::ArrowUpRight) + .tab_index(0_isize) + .icon_position(IconPosition::End) + .icon_color(Color::Muted) + .icon_size(IconSize::Small) + .style(ButtonStyle::OutlinedGhost) + .size(ButtonSize::Medium) + .on_click({ + let on_click = action_link.on_click.clone(); + cx.listener(move |this, _, window, cx| { + on_click(this, window, cx); + }) + }), + ), + ) + .when(!is_last, |this| this.child(Divider::horizontal())) + .into_any_element(), } } } @@ -1207,6 +1260,20 @@ impl PartialEq for SubPageLink { } } +#[derive(Clone)] +struct ActionLink { + title: SharedString, + description: Option, + button_text: SharedString, + on_click: Arc, +} + +impl PartialEq for ActionLink { + fn eq(&self, other: &Self) -> bool { + self.title == other.title + } +} + fn all_language_names(cx: &App) -> Vec { workspace::AppState::global(cx) .upgrade() @@ -1626,6 +1693,9 @@ impl SettingsWindow { any_found_since_last_header = true; } } + SettingsPageItem::ActionLink(_) => { + any_found_since_last_header = true; + } } } if let Some(last_header) = page_filter.get_mut(header_index) @@ -1864,6 +1934,18 @@ impl SettingsWindow { sub_page_link.title.as_ref(), ); } + SettingsPageItem::ActionLink(action_link) => { + documents.push(bm25::Document { + id: key_index, + contents: [page.title, header_str, action_link.title.as_ref()] + .join("\n"), + }); + push_candidates( + &mut fuzzy_match_candidates, + key_index, + action_link.title.as_ref(), + ); + } } push_candidates(&mut fuzzy_match_candidates, key_index, page.title); push_candidates(&mut fuzzy_match_candidates, key_index, header_str);