keymap_ui: Hide tooltips when context menu is shown (#34286)
This PR ensures tooltips are dismissed/not shown once the context menu is opened. It also ensures the context menu is dismissed once the list is scrolled. Release Notes: - N/A
This commit is contained in:
parent
d1a6c5d494
commit
a58a75c0f6
1 changed files with 124 additions and 80 deletions
|
@ -11,8 +11,8 @@ use fs::Fs;
|
||||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
Action, AppContext as _, AsyncApp, ClickEvent, Context, DismissEvent, Entity, EventEmitter,
|
Action, AppContext as _, AsyncApp, ClickEvent, Context, DismissEvent, Entity, EventEmitter,
|
||||||
FocusHandle, Focusable, Global, KeyContext, Keystroke, ModifiersChangedEvent, ScrollStrategy,
|
FocusHandle, Focusable, Global, KeyContext, Keystroke, ModifiersChangedEvent, MouseButton,
|
||||||
StyledText, Subscription, WeakEntity, actions, div,
|
Point, ScrollStrategy, StyledText, Subscription, WeakEntity, actions, anchored, deferred, div,
|
||||||
};
|
};
|
||||||
use language::{Language, LanguageConfig, ToOffset as _};
|
use language::{Language, LanguageConfig, ToOffset as _};
|
||||||
use settings::{BaseKeymap, KeybindSource, KeymapFile, SettingsAssets};
|
use settings::{BaseKeymap, KeybindSource, KeymapFile, SettingsAssets};
|
||||||
|
@ -21,7 +21,7 @@ use util::ResultExt;
|
||||||
|
|
||||||
use ui::{
|
use ui::{
|
||||||
ActiveTheme as _, App, Banner, BorrowAppContext, ContextMenu, ParentElement as _, Render,
|
ActiveTheme as _, App, Banner, BorrowAppContext, ContextMenu, ParentElement as _, Render,
|
||||||
SharedString, Styled as _, Tooltip, Window, prelude::*, right_click_menu,
|
SharedString, Styled as _, Tooltip, Window, prelude::*,
|
||||||
};
|
};
|
||||||
use workspace::{
|
use workspace::{
|
||||||
Item, ModalView, SerializableItem, Workspace, notifications::NotifyTaskExt as _,
|
Item, ModalView, SerializableItem, Workspace, notifications::NotifyTaskExt as _,
|
||||||
|
@ -187,7 +187,7 @@ struct ConflictState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConflictState {
|
impl ConflictState {
|
||||||
fn new(key_bindings: &Vec<ProcessedKeybinding>) -> Self {
|
fn new(key_bindings: &[ProcessedKeybinding]) -> Self {
|
||||||
let mut action_keybind_mapping: HashMap<_, Vec<usize>> = HashMap::default();
|
let mut action_keybind_mapping: HashMap<_, Vec<usize>> = HashMap::default();
|
||||||
|
|
||||||
key_bindings
|
key_bindings
|
||||||
|
@ -255,6 +255,7 @@ struct KeymapEditor {
|
||||||
filter_editor: Entity<Editor>,
|
filter_editor: Entity<Editor>,
|
||||||
keystroke_editor: Entity<KeystrokeInput>,
|
keystroke_editor: Entity<KeystrokeInput>,
|
||||||
selected_index: Option<usize>,
|
selected_index: Option<usize>,
|
||||||
|
context_menu: Option<(Entity<ContextMenu>, Point<Pixels>, Subscription)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventEmitter<()> for KeymapEditor {}
|
impl EventEmitter<()> for KeymapEditor {}
|
||||||
|
@ -267,8 +268,6 @@ impl Focusable for KeymapEditor {
|
||||||
|
|
||||||
impl KeymapEditor {
|
impl KeymapEditor {
|
||||||
fn new(workspace: WeakEntity<Workspace>, window: &mut Window, cx: &mut Context<Self>) -> Self {
|
fn new(workspace: WeakEntity<Workspace>, window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||||
let focus_handle = cx.focus_handle();
|
|
||||||
|
|
||||||
let _keymap_subscription =
|
let _keymap_subscription =
|
||||||
cx.observe_global::<KeymapEventChannel>(Self::update_keybindings);
|
cx.observe_global::<KeymapEventChannel>(Self::update_keybindings);
|
||||||
let table_interaction_state = TableInteractionState::new(window, cx);
|
let table_interaction_state = TableInteractionState::new(window, cx);
|
||||||
|
@ -311,12 +310,13 @@ impl KeymapEditor {
|
||||||
search_mode: SearchMode::default(),
|
search_mode: SearchMode::default(),
|
||||||
string_match_candidates: Arc::new(vec![]),
|
string_match_candidates: Arc::new(vec![]),
|
||||||
matches: vec![],
|
matches: vec![],
|
||||||
focus_handle: focus_handle.clone(),
|
focus_handle: cx.focus_handle(),
|
||||||
_keymap_subscription,
|
_keymap_subscription,
|
||||||
table_interaction_state,
|
table_interaction_state,
|
||||||
filter_editor,
|
filter_editor,
|
||||||
keystroke_editor,
|
keystroke_editor,
|
||||||
selected_index: None,
|
selected_index: None,
|
||||||
|
context_menu: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.update_keybindings(cx);
|
this.update_keybindings(cx);
|
||||||
|
@ -593,6 +593,68 @@ impl KeymapEditor {
|
||||||
.and_then(|keybind_index| self.keybindings.get(keybind_index))
|
.and_then(|keybind_index| self.keybindings.get(keybind_index))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn select_index(&mut self, index: usize, cx: &mut Context<Self>) {
|
||||||
|
if self.selected_index != Some(index) {
|
||||||
|
self.selected_index = Some(index);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_context_menu(
|
||||||
|
&mut self,
|
||||||
|
position: Point<Pixels>,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut Context<Self>,
|
||||||
|
) {
|
||||||
|
self.context_menu = self.selected_binding().map(|selected_binding| {
|
||||||
|
let selected_binding_has_no_context = selected_binding
|
||||||
|
.context
|
||||||
|
.as_ref()
|
||||||
|
.and_then(KeybindContextString::local)
|
||||||
|
.is_none();
|
||||||
|
|
||||||
|
let selected_binding_is_unbound = selected_binding.ui_key_binding.is_none();
|
||||||
|
|
||||||
|
let context_menu = ContextMenu::build(window, cx, |menu, _window, _cx| {
|
||||||
|
menu.action_disabled_when(
|
||||||
|
selected_binding_is_unbound,
|
||||||
|
"Edit",
|
||||||
|
Box::new(EditBinding),
|
||||||
|
)
|
||||||
|
.action("Create", Box::new(CreateBinding))
|
||||||
|
.action("Copy action", Box::new(CopyAction))
|
||||||
|
.action_disabled_when(
|
||||||
|
selected_binding_has_no_context,
|
||||||
|
"Copy Context",
|
||||||
|
Box::new(CopyContext),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let context_menu_handle = context_menu.focus_handle(cx);
|
||||||
|
window.defer(cx, move |window, _cx| window.focus(&context_menu_handle));
|
||||||
|
let subscription = cx.subscribe_in(
|
||||||
|
&context_menu,
|
||||||
|
window,
|
||||||
|
|this, _, _: &DismissEvent, window, cx| {
|
||||||
|
this.dismiss_context_menu(window, cx);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
(context_menu, position, subscription)
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dismiss_context_menu(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
self.context_menu.take();
|
||||||
|
window.focus(&self.focus_handle);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn context_menu_deployed(&self) -> bool {
|
||||||
|
self.context_menu.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
fn select_next(&mut self, _: &menu::SelectNext, window: &mut Window, cx: &mut Context<Self>) {
|
fn select_next(&mut self, _: &menu::SelectNext, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
if let Some(selected) = self.selected_index {
|
if let Some(selected) = self.selected_index {
|
||||||
let selected = selected + 1;
|
let selected = selected + 1;
|
||||||
|
@ -975,6 +1037,7 @@ impl Render for KeymapEditor {
|
||||||
"keymap-editor-table",
|
"keymap-editor-table",
|
||||||
row_count,
|
row_count,
|
||||||
cx.processor(move |this, range: Range<usize>, _window, cx| {
|
cx.processor(move |this, range: Range<usize>, _window, cx| {
|
||||||
|
let context_menu_deployed = this.context_menu_deployed();
|
||||||
range
|
range
|
||||||
.filter_map(|index| {
|
.filter_map(|index| {
|
||||||
let candidate_id = this.matches.get(index)?.candidate_id;
|
let candidate_id = this.matches.get(index)?.candidate_id;
|
||||||
|
@ -983,7 +1046,8 @@ impl Render for KeymapEditor {
|
||||||
let action = div()
|
let action = div()
|
||||||
.child(binding.action_name.clone())
|
.child(binding.action_name.clone())
|
||||||
.id(("keymap action", index))
|
.id(("keymap action", index))
|
||||||
.tooltip({
|
.when(!context_menu_deployed, |this| {
|
||||||
|
this.tooltip({
|
||||||
let action_name = binding.action_name.clone();
|
let action_name = binding.action_name.clone();
|
||||||
let action_docs = binding.action_docs;
|
let action_docs = binding.action_docs;
|
||||||
move |_, cx| {
|
move |_, cx| {
|
||||||
|
@ -999,6 +1063,7 @@ impl Render for KeymapEditor {
|
||||||
cx.new(|_| action_tooltip).into()
|
cx.new(|_| action_tooltip).into()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
})
|
||||||
.into_any_element();
|
.into_any_element();
|
||||||
let keystrokes = binding.ui_key_binding.clone().map_or(
|
let keystrokes = binding.ui_key_binding.clone().map_or(
|
||||||
binding.keystroke_text.clone().into_any_element(),
|
binding.keystroke_text.clone().into_any_element(),
|
||||||
|
@ -1023,7 +1088,7 @@ impl Render for KeymapEditor {
|
||||||
div()
|
div()
|
||||||
.id(("keymap context", index))
|
.id(("keymap context", index))
|
||||||
.child(context.clone())
|
.child(context.clone())
|
||||||
.when(is_local, |this| {
|
.when(is_local && !context_menu_deployed, |this| {
|
||||||
this.tooltip(Tooltip::element({
|
this.tooltip(Tooltip::element({
|
||||||
move |_, _| {
|
move |_, _| {
|
||||||
context.clone().into_any_element()
|
context.clone().into_any_element()
|
||||||
|
@ -1055,9 +1120,30 @@ impl Render for KeymapEditor {
|
||||||
|
|
||||||
let row = row
|
let row = row
|
||||||
.id(("keymap-table-row", row_index))
|
.id(("keymap-table-row", row_index))
|
||||||
|
.on_any_mouse_down(cx.listener(
|
||||||
|
move |this,
|
||||||
|
mouse_down_event: &gpui::MouseDownEvent,
|
||||||
|
window,
|
||||||
|
cx| {
|
||||||
|
match mouse_down_event.button {
|
||||||
|
MouseButton::Left => {
|
||||||
|
this.select_index(row_index, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseButton::Right => {
|
||||||
|
this.select_index(row_index, cx);
|
||||||
|
this.create_context_menu(
|
||||||
|
mouse_down_event.position,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
))
|
||||||
.on_click(cx.listener(
|
.on_click(cx.listener(
|
||||||
move |this, event: &ClickEvent, window, cx| {
|
move |this, event: &ClickEvent, window, cx| {
|
||||||
this.selected_index = Some(row_index);
|
|
||||||
if event.up.click_count == 2 {
|
if event.up.click_count == 2 {
|
||||||
this.open_edit_keybinding_modal(false, window, cx);
|
this.open_edit_keybinding_modal(false, window, cx);
|
||||||
}
|
}
|
||||||
|
@ -1071,18 +1157,23 @@ impl Render for KeymapEditor {
|
||||||
row.border_color(cx.theme().colors().panel_focused_border)
|
row.border_color(cx.theme().colors().panel_focused_border)
|
||||||
});
|
});
|
||||||
|
|
||||||
right_click_menu(("keymap-table-row-menu", row_index))
|
row.into_any_element()
|
||||||
.trigger(move |_, _, _| row)
|
|
||||||
.menu({
|
|
||||||
let this = cx.weak_entity();
|
|
||||||
move |window, cx| {
|
|
||||||
build_keybind_context_menu(&this, row_index, window, cx)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.into_any_element()
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
.on_scroll_wheel(cx.listener(|this, _, _, cx| {
|
||||||
|
this.context_menu.take();
|
||||||
|
cx.notify();
|
||||||
|
}))
|
||||||
|
.children(self.context_menu.as_ref().map(|(menu, position, _)| {
|
||||||
|
deferred(
|
||||||
|
anchored()
|
||||||
|
.position(*position)
|
||||||
|
.anchor(gpui::Corner::TopLeft)
|
||||||
|
.child(menu.clone()),
|
||||||
|
)
|
||||||
|
.with_priority(1)
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1895,53 +1986,6 @@ impl Render for KeystrokeInput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_keybind_context_menu(
|
|
||||||
this: &WeakEntity<KeymapEditor>,
|
|
||||||
item_idx: usize,
|
|
||||||
window: &mut Window,
|
|
||||||
cx: &mut App,
|
|
||||||
) -> Entity<ContextMenu> {
|
|
||||||
ContextMenu::build(window, cx, |menu, _window, cx| {
|
|
||||||
let selected_binding = this
|
|
||||||
.update(cx, |this, _cx| {
|
|
||||||
this.selected_index = Some(item_idx);
|
|
||||||
this.selected_binding().cloned()
|
|
||||||
})
|
|
||||||
.ok()
|
|
||||||
.flatten();
|
|
||||||
|
|
||||||
let Some(selected_binding) = selected_binding else {
|
|
||||||
return menu;
|
|
||||||
};
|
|
||||||
|
|
||||||
let selected_binding_has_no_context = selected_binding
|
|
||||||
.context
|
|
||||||
.as_ref()
|
|
||||||
.and_then(KeybindContextString::local)
|
|
||||||
.is_none();
|
|
||||||
|
|
||||||
let selected_binding_is_unbound_action = selected_binding.ui_key_binding.is_none();
|
|
||||||
|
|
||||||
menu.action_disabled_when(
|
|
||||||
selected_binding_is_unbound_action,
|
|
||||||
"Edit",
|
|
||||||
Box::new(EditBinding),
|
|
||||||
)
|
|
||||||
.action("Create", Box::new(CreateBinding))
|
|
||||||
.action_disabled_when(
|
|
||||||
selected_binding_is_unbound_action,
|
|
||||||
"Delete",
|
|
||||||
Box::new(DeleteBinding),
|
|
||||||
)
|
|
||||||
.action("Copy action", Box::new(CopyAction))
|
|
||||||
.action_disabled_when(
|
|
||||||
selected_binding_has_no_context,
|
|
||||||
"Copy Context",
|
|
||||||
Box::new(CopyContext),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn collect_contexts_from_assets() -> Vec<SharedString> {
|
fn collect_contexts_from_assets() -> Vec<SharedString> {
|
||||||
let mut keymap_assets = vec![
|
let mut keymap_assets = vec![
|
||||||
util::asset_str::<SettingsAssets>(settings::DEFAULT_KEYMAP_PATH),
|
util::asset_str::<SettingsAssets>(settings::DEFAULT_KEYMAP_PATH),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue