@@ -15,10 +15,10 @@ use crate::{
use anyhow::Result;
use collections::{BTreeMap, HashMap};
use gpui::{
- black, hsla, point, px, relative, size, transparent_black, Action, AnyElement, AvailableSpace,
- BorrowAppContext, BorrowWindow, Bounds, ContentMask, Corners, DispatchPhase, Edges, Element,
- ElementId, ElementInputHandler, Entity, FocusHandle, GlobalElementId, Hsla, InputHandler,
- KeyContext, KeyDownEvent, KeyListener, KeyMatch, Line, LineLayout, Modifiers, MouseButton,
+ black, hsla, point, px, relative, size, transparent_black, Action, ActionListener, AnyElement,
+ AvailableSpace, BorrowAppContext, BorrowWindow, Bounds, ContentMask, Corners, DispatchPhase,
+ Edges, Element, ElementId, ElementInputHandler, Entity, FocusHandle, GlobalElementId, Hsla,
+ InputHandler, KeyContext, KeyDownEvent, KeyMatch, Line, LineLayout, Modifiers, MouseButton,
MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, ScrollWheelEvent, ShapedGlyph, Size,
Style, TextRun, TextStyle, TextSystem, ViewContext, WindowContext, WrappedLineLayout,
};
@@ -2459,7 +2459,166 @@ impl Element<Editor> for EditorElement {
cx.with_key_dispatch(
dispatch_context,
Some(editor.focus_handle.clone()),
- |_, _| {},
+ |_, cx| {
+ handle_action(cx, Editor::move_left);
+ handle_action(cx, Editor::move_right);
+ handle_action(cx, Editor::move_down);
+ handle_action(cx, Editor::move_up);
+ // on_action(cx, Editor::new_file); todo!()
+ // on_action(cx, Editor::new_file_in_direction); todo!()
+ handle_action(cx, Editor::cancel);
+ handle_action(cx, Editor::newline);
+ handle_action(cx, Editor::newline_above);
+ handle_action(cx, Editor::newline_below);
+ handle_action(cx, Editor::backspace);
+ handle_action(cx, Editor::delete);
+ handle_action(cx, Editor::tab);
+ handle_action(cx, Editor::tab_prev);
+ handle_action(cx, Editor::indent);
+ handle_action(cx, Editor::outdent);
+ handle_action(cx, Editor::delete_line);
+ handle_action(cx, Editor::join_lines);
+ handle_action(cx, Editor::sort_lines_case_sensitive);
+ handle_action(cx, Editor::sort_lines_case_insensitive);
+ handle_action(cx, Editor::reverse_lines);
+ handle_action(cx, Editor::shuffle_lines);
+ handle_action(cx, Editor::convert_to_upper_case);
+ handle_action(cx, Editor::convert_to_lower_case);
+ handle_action(cx, Editor::convert_to_title_case);
+ handle_action(cx, Editor::convert_to_snake_case);
+ handle_action(cx, Editor::convert_to_kebab_case);
+ handle_action(cx, Editor::convert_to_upper_camel_case);
+ handle_action(cx, Editor::convert_to_lower_camel_case);
+ handle_action(cx, Editor::delete_to_previous_word_start);
+ handle_action(cx, Editor::delete_to_previous_subword_start);
+ handle_action(cx, Editor::delete_to_next_word_end);
+ handle_action(cx, Editor::delete_to_next_subword_end);
+ handle_action(cx, Editor::delete_to_beginning_of_line);
+ handle_action(cx, Editor::delete_to_end_of_line);
+ handle_action(cx, Editor::cut_to_end_of_line);
+ handle_action(cx, Editor::duplicate_line);
+ handle_action(cx, Editor::move_line_up);
+ handle_action(cx, Editor::move_line_down);
+ handle_action(cx, Editor::transpose);
+ handle_action(cx, Editor::cut);
+ handle_action(cx, Editor::copy);
+ handle_action(cx, Editor::paste);
+ handle_action(cx, Editor::undo);
+ handle_action(cx, Editor::redo);
+ handle_action(cx, Editor::move_page_up);
+ handle_action(cx, Editor::move_page_down);
+ handle_action(cx, Editor::next_screen);
+ handle_action(cx, Editor::scroll_cursor_top);
+ handle_action(cx, Editor::scroll_cursor_center);
+ handle_action(cx, Editor::scroll_cursor_bottom);
+ handle_action(cx, |editor, _: &LineDown, cx| {
+ editor.scroll_screen(&ScrollAmount::Line(1.), cx)
+ });
+ handle_action(cx, |editor, _: &LineUp, cx| {
+ editor.scroll_screen(&ScrollAmount::Line(-1.), cx)
+ });
+ handle_action(cx, |editor, _: &HalfPageDown, cx| {
+ editor.scroll_screen(&ScrollAmount::Page(0.5), cx)
+ });
+ handle_action(cx, |editor, _: &HalfPageUp, cx| {
+ editor.scroll_screen(&ScrollAmount::Page(-0.5), cx)
+ });
+ handle_action(cx, |editor, _: &PageDown, cx| {
+ editor.scroll_screen(&ScrollAmount::Page(1.), cx)
+ });
+ handle_action(cx, |editor, _: &PageUp, cx| {
+ editor.scroll_screen(&ScrollAmount::Page(-1.), cx)
+ });
+ handle_action(cx, Editor::move_to_previous_word_start);
+ handle_action(cx, Editor::move_to_previous_subword_start);
+ handle_action(cx, Editor::move_to_next_word_end);
+ handle_action(cx, Editor::move_to_next_subword_end);
+ handle_action(cx, Editor::move_to_beginning_of_line);
+ handle_action(cx, Editor::move_to_end_of_line);
+ handle_action(cx, Editor::move_to_start_of_paragraph);
+ handle_action(cx, Editor::move_to_end_of_paragraph);
+ handle_action(cx, Editor::move_to_beginning);
+ handle_action(cx, Editor::move_to_end);
+ handle_action(cx, Editor::select_up);
+ handle_action(cx, Editor::select_down);
+ handle_action(cx, Editor::select_left);
+ handle_action(cx, Editor::select_right);
+ handle_action(cx, Editor::select_to_previous_word_start);
+ handle_action(cx, Editor::select_to_previous_subword_start);
+ handle_action(cx, Editor::select_to_next_word_end);
+ handle_action(cx, Editor::select_to_next_subword_end);
+ handle_action(cx, Editor::select_to_beginning_of_line);
+ handle_action(cx, Editor::select_to_end_of_line);
+ handle_action(cx, Editor::select_to_start_of_paragraph);
+ handle_action(cx, Editor::select_to_end_of_paragraph);
+ handle_action(cx, Editor::select_to_beginning);
+ handle_action(cx, Editor::select_to_end);
+ handle_action(cx, Editor::select_all);
+ handle_action(cx, |editor, action, cx| {
+ editor.select_all_matches(action, cx).log_err();
+ });
+ handle_action(cx, Editor::select_line);
+ handle_action(cx, Editor::split_selection_into_lines);
+ handle_action(cx, Editor::add_selection_above);
+ handle_action(cx, Editor::add_selection_below);
+ handle_action(cx, |editor, action, cx| {
+ editor.select_next(action, cx).log_err();
+ });
+ handle_action(cx, |editor, action, cx| {
+ editor.select_previous(action, cx).log_err();
+ });
+ handle_action(cx, Editor::toggle_comments);
+ handle_action(cx, Editor::select_larger_syntax_node);
+ handle_action(cx, Editor::select_smaller_syntax_node);
+ handle_action(cx, Editor::move_to_enclosing_bracket);
+ handle_action(cx, Editor::undo_selection);
+ handle_action(cx, Editor::redo_selection);
+ handle_action(cx, Editor::go_to_diagnostic);
+ handle_action(cx, Editor::go_to_prev_diagnostic);
+ handle_action(cx, Editor::go_to_hunk);
+ handle_action(cx, Editor::go_to_prev_hunk);
+ handle_action(cx, Editor::go_to_definition);
+ handle_action(cx, Editor::go_to_definition_split);
+ handle_action(cx, Editor::go_to_type_definition);
+ handle_action(cx, Editor::go_to_type_definition_split);
+ handle_action(cx, Editor::fold);
+ handle_action(cx, Editor::fold_at);
+ handle_action(cx, Editor::unfold_lines);
+ handle_action(cx, Editor::unfold_at);
+ handle_action(cx, Editor::fold_selected_ranges);
+ handle_action(cx, Editor::show_completions);
+ handle_action(cx, Editor::toggle_code_actions);
+ // on_action(cx, Editor::open_excerpts); todo!()
+ handle_action(cx, Editor::toggle_soft_wrap);
+ handle_action(cx, Editor::toggle_inlay_hints);
+ handle_action(cx, Editor::reveal_in_finder);
+ handle_action(cx, Editor::copy_path);
+ handle_action(cx, Editor::copy_relative_path);
+ handle_action(cx, Editor::copy_highlight_json);
+ handle_action(cx, |editor, action, cx| {
+ editor
+ .format(action, cx)
+ .map(|task| task.detach_and_log_err(cx));
+ });
+ handle_action(cx, Editor::restart_language_server);
+ handle_action(cx, Editor::show_character_palette);
+ // on_action(cx, Editor::confirm_completion); todo!()
+ handle_action(cx, |editor, action, cx| {
+ editor
+ .confirm_code_action(action, cx)
+ .map(|task| task.detach_and_log_err(cx));
+ });
+ // on_action(cx, Editor::rename); todo!()
+ // on_action(cx, Editor::confirm_rename); todo!()
+ // on_action(cx, Editor::find_all_references); todo!()
+ handle_action(cx, Editor::next_copilot_suggestion);
+ handle_action(cx, Editor::previous_copilot_suggestion);
+ handle_action(cx, Editor::copilot_suggest);
+ handle_action(cx, Editor::context_menu_first);
+ handle_action(cx, Editor::context_menu_prev);
+ handle_action(cx, Editor::context_menu_next);
+ handle_action(cx, Editor::context_menu_last);
+ },
)
});
}
@@ -3995,197 +4154,14 @@ fn scale_horizontal_mouse_autoscroll_delta(delta: Pixels) -> f32 {
// }
// }
-fn build_key_listeners(
- global_element_id: GlobalElementId,
-) -> impl IntoIterator<Item = (TypeId, KeyListener<Editor>)> {
- [
- build_action_listener(Editor::move_left),
- build_action_listener(Editor::move_right),
- build_action_listener(Editor::move_down),
- build_action_listener(Editor::move_up),
- // build_action_listener(Editor::new_file), todo!()
- // build_action_listener(Editor::new_file_in_direction), todo!()
- build_action_listener(Editor::cancel),
- build_action_listener(Editor::newline),
- build_action_listener(Editor::newline_above),
- build_action_listener(Editor::newline_below),
- build_action_listener(Editor::backspace),
- build_action_listener(Editor::delete),
- build_action_listener(Editor::tab),
- build_action_listener(Editor::tab_prev),
- build_action_listener(Editor::indent),
- build_action_listener(Editor::outdent),
- build_action_listener(Editor::delete_line),
- build_action_listener(Editor::join_lines),
- build_action_listener(Editor::sort_lines_case_sensitive),
- build_action_listener(Editor::sort_lines_case_insensitive),
- build_action_listener(Editor::reverse_lines),
- build_action_listener(Editor::shuffle_lines),
- build_action_listener(Editor::convert_to_upper_case),
- build_action_listener(Editor::convert_to_lower_case),
- build_action_listener(Editor::convert_to_title_case),
- build_action_listener(Editor::convert_to_snake_case),
- build_action_listener(Editor::convert_to_kebab_case),
- build_action_listener(Editor::convert_to_upper_camel_case),
- build_action_listener(Editor::convert_to_lower_camel_case),
- build_action_listener(Editor::delete_to_previous_word_start),
- build_action_listener(Editor::delete_to_previous_subword_start),
- build_action_listener(Editor::delete_to_next_word_end),
- build_action_listener(Editor::delete_to_next_subword_end),
- build_action_listener(Editor::delete_to_beginning_of_line),
- build_action_listener(Editor::delete_to_end_of_line),
- build_action_listener(Editor::cut_to_end_of_line),
- build_action_listener(Editor::duplicate_line),
- build_action_listener(Editor::move_line_up),
- build_action_listener(Editor::move_line_down),
- build_action_listener(Editor::transpose),
- build_action_listener(Editor::cut),
- build_action_listener(Editor::copy),
- build_action_listener(Editor::paste),
- build_action_listener(Editor::undo),
- build_action_listener(Editor::redo),
- build_action_listener(Editor::move_page_up),
- build_action_listener(Editor::move_page_down),
- build_action_listener(Editor::next_screen),
- build_action_listener(Editor::scroll_cursor_top),
- build_action_listener(Editor::scroll_cursor_center),
- build_action_listener(Editor::scroll_cursor_bottom),
- build_action_listener(|editor, _: &LineDown, cx| {
- editor.scroll_screen(&ScrollAmount::Line(1.), cx)
- }),
- build_action_listener(|editor, _: &LineUp, cx| {
- editor.scroll_screen(&ScrollAmount::Line(-1.), cx)
- }),
- build_action_listener(|editor, _: &HalfPageDown, cx| {
- editor.scroll_screen(&ScrollAmount::Page(0.5), cx)
- }),
- build_action_listener(|editor, _: &HalfPageUp, cx| {
- editor.scroll_screen(&ScrollAmount::Page(-0.5), cx)
- }),
- build_action_listener(|editor, _: &PageDown, cx| {
- editor.scroll_screen(&ScrollAmount::Page(1.), cx)
- }),
- build_action_listener(|editor, _: &PageUp, cx| {
- editor.scroll_screen(&ScrollAmount::Page(-1.), cx)
- }),
- build_action_listener(Editor::move_to_previous_word_start),
- build_action_listener(Editor::move_to_previous_subword_start),
- build_action_listener(Editor::move_to_next_word_end),
- build_action_listener(Editor::move_to_next_subword_end),
- build_action_listener(Editor::move_to_beginning_of_line),
- build_action_listener(Editor::move_to_end_of_line),
- build_action_listener(Editor::move_to_start_of_paragraph),
- build_action_listener(Editor::move_to_end_of_paragraph),
- build_action_listener(Editor::move_to_beginning),
- build_action_listener(Editor::move_to_end),
- build_action_listener(Editor::select_up),
- build_action_listener(Editor::select_down),
- build_action_listener(Editor::select_left),
- build_action_listener(Editor::select_right),
- build_action_listener(Editor::select_to_previous_word_start),
- build_action_listener(Editor::select_to_previous_subword_start),
- build_action_listener(Editor::select_to_next_word_end),
- build_action_listener(Editor::select_to_next_subword_end),
- build_action_listener(Editor::select_to_beginning_of_line),
- build_action_listener(Editor::select_to_end_of_line),
- build_action_listener(Editor::select_to_start_of_paragraph),
- build_action_listener(Editor::select_to_end_of_paragraph),
- build_action_listener(Editor::select_to_beginning),
- build_action_listener(Editor::select_to_end),
- build_action_listener(Editor::select_all),
- build_action_listener(|editor, action, cx| {
- editor.select_all_matches(action, cx).log_err();
- }),
- build_action_listener(Editor::select_line),
- build_action_listener(Editor::split_selection_into_lines),
- build_action_listener(Editor::add_selection_above),
- build_action_listener(Editor::add_selection_below),
- build_action_listener(|editor, action, cx| {
- editor.select_next(action, cx).log_err();
- }),
- build_action_listener(|editor, action, cx| {
- editor.select_previous(action, cx).log_err();
- }),
- build_action_listener(Editor::toggle_comments),
- build_action_listener(Editor::select_larger_syntax_node),
- build_action_listener(Editor::select_smaller_syntax_node),
- build_action_listener(Editor::move_to_enclosing_bracket),
- build_action_listener(Editor::undo_selection),
- build_action_listener(Editor::redo_selection),
- build_action_listener(Editor::go_to_diagnostic),
- build_action_listener(Editor::go_to_prev_diagnostic),
- build_action_listener(Editor::go_to_hunk),
- build_action_listener(Editor::go_to_prev_hunk),
- build_action_listener(Editor::go_to_definition),
- build_action_listener(Editor::go_to_definition_split),
- build_action_listener(Editor::go_to_type_definition),
- build_action_listener(Editor::go_to_type_definition_split),
- build_action_listener(Editor::fold),
- build_action_listener(Editor::fold_at),
- build_action_listener(Editor::unfold_lines),
- build_action_listener(Editor::unfold_at),
- build_action_listener(Editor::fold_selected_ranges),
- build_action_listener(Editor::show_completions),
- build_action_listener(Editor::toggle_code_actions),
- // build_action_listener(Editor::open_excerpts), todo!()
- build_action_listener(Editor::toggle_soft_wrap),
- build_action_listener(Editor::toggle_inlay_hints),
- build_action_listener(Editor::reveal_in_finder),
- build_action_listener(Editor::copy_path),
- build_action_listener(Editor::copy_relative_path),
- build_action_listener(Editor::copy_highlight_json),
- build_action_listener(|editor, action, cx| {
- editor
- .format(action, cx)
- .map(|task| task.detach_and_log_err(cx));
- }),
- build_action_listener(Editor::restart_language_server),
- build_action_listener(Editor::show_character_palette),
- // build_action_listener(Editor::confirm_completion), todo!()
- build_action_listener(|editor, action, cx| {
- editor
- .confirm_code_action(action, cx)
- .map(|task| task.detach_and_log_err(cx));
- }),
- // build_action_listener(Editor::rename), todo!()
- // build_action_listener(Editor::confirm_rename), todo!()
- // build_action_listener(Editor::find_all_references), todo!()
- build_action_listener(Editor::next_copilot_suggestion),
- build_action_listener(Editor::previous_copilot_suggestion),
- build_action_listener(Editor::copilot_suggest),
- build_action_listener(Editor::context_menu_first),
- build_action_listener(Editor::context_menu_prev),
- build_action_listener(Editor::context_menu_next),
- build_action_listener(Editor::context_menu_last),
- ]
-}
-
-fn build_key_listener<T: 'static>(
- listener: impl Fn(
- &mut Editor,
- &T,
- &[&KeyContext],
- DispatchPhase,
- &mut ViewContext<Editor>,
- ) -> Option<Box<dyn Action>>
- + 'static,
-) -> (TypeId, KeyListener<Editor>) {
- (
- TypeId::of::<T>(),
- Box::new(move |editor, event, dispatch_context, phase, cx| {
- let key_event = event.downcast_ref::<T>()?;
- listener(editor, key_event, dispatch_context, phase, cx)
- }),
- )
-}
-
-fn build_action_listener<T: Action>(
+fn handle_action<T: Action>(
+ cx: &mut ViewContext<Editor>,
listener: impl Fn(&mut Editor, &T, &mut ViewContext<Editor>) + 'static,
-) -> (TypeId, KeyListener<Editor>) {
- build_key_listener(move |editor, action: &T, dispatch_context, phase, cx| {
+) {
+ cx.on_action(TypeId::of::<T>(), move |editor, action, phase, cx| {
+ let action = action.downcast_ref().unwrap();
if phase == DispatchPhase::Bubble {
listener(editor, action, cx);
}
- None
})
}
@@ -165,43 +165,40 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
}
/// Capture the given action, fires during the capture phase
- fn capture_action<A: 'static>(
+ fn capture_action<A: Action>(
mut self,
listener: impl Fn(&mut V, &A, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
- self.stateless_interactivity().key_listeners.push((
+ self.stateless_interactivity().action_listeners.push((
TypeId::of::<A>(),
- Box::new(move |view, action, _dipatch_context, phase, cx| {
+ Box::new(move |view, action, phase, cx| {
let action = action.downcast_ref().unwrap();
if phase == DispatchPhase::Capture {
listener(view, action, cx)
}
- None
}),
));
self
}
/// Add a listener for the given action, fires during the bubble event phase
- fn on_action<A: 'static>(
+ fn on_action<A: Action>(
mut self,
listener: impl Fn(&mut V, &A, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
- self.stateless_interactivity().key_listeners.push((
+ self.stateless_interactivity().action_listeners.push((
TypeId::of::<A>(),
- Box::new(move |view, action, _dispatch_context, phase, cx| {
+ Box::new(move |view, action, phase, cx| {
let action = action.downcast_ref().unwrap();
if phase == DispatchPhase::Bubble {
listener(view, action, cx)
}
-
- None
}),
));
self
@@ -214,14 +211,11 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
- self.stateless_interactivity().key_listeners.push((
- TypeId::of::<KeyDownEvent>(),
- Box::new(move |view, event, _, phase, cx| {
- let event = event.downcast_ref().unwrap();
- listener(view, event, phase, cx);
- None
- }),
- ));
+ self.stateless_interactivity()
+ .key_down_listeners
+ .push(Box::new(move |view, event, phase, cx| {
+ listener(view, event, phase, cx)
+ }));
self
}
@@ -232,14 +226,11 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
- self.stateless_interactivity().key_listeners.push((
- TypeId::of::<KeyUpEvent>(),
- Box::new(move |view, event, _, phase, cx| {
- let event = event.downcast_ref().unwrap();
- listener(view, event, phase, cx);
- None
- }),
- ));
+ self.stateless_interactivity()
+ .key_up_listeners
+ .push(Box::new(move |view, event, phase, cx| {
+ listener(view, event, phase, cx)
+ }));
self
}
@@ -439,6 +430,26 @@ pub trait ElementInteractivity<V: 'static>: 'static {
}
}
+ fn initialize(&mut self, cx: &mut ViewContext<V>) {
+ let stateless = self.as_stateless_mut();
+
+ for listener in stateless.key_down_listeners.drain(..) {
+ cx.on_key_event(move |state, event: &KeyDownEvent, phase, cx| {
+ listener(state, event, phase, cx);
+ })
+ }
+
+ for listener in stateless.key_up_listeners.drain(..) {
+ cx.on_key_event(move |state, event: &KeyUpEvent, phase, cx| {
+ listener(state, event, phase, cx);
+ })
+ }
+
+ for (action_type, listener) in stateless.action_listeners.drain(..) {
+ cx.on_action(action_type, listener)
+ }
+ }
+
fn paint(
&mut self,
bounds: Bounds<Pixels>,
@@ -765,7 +776,9 @@ pub struct StatelessInteractivity<V> {
pub mouse_up_listeners: SmallVec<[MouseUpListener<V>; 2]>,
pub mouse_move_listeners: SmallVec<[MouseMoveListener<V>; 2]>,
pub scroll_wheel_listeners: SmallVec<[ScrollWheelListener<V>; 2]>,
- pub key_listeners: SmallVec<[(TypeId, KeyListener<V>); 32]>,
+ pub key_down_listeners: SmallVec<[KeyDownListener<V>; 2]>,
+ pub key_up_listeners: SmallVec<[KeyUpListener<V>; 2]>,
+ pub action_listeners: SmallVec<[(TypeId, ActionListener<V>); 8]>,
pub hover_style: StyleRefinement,
pub group_hover_style: Option<GroupStyle>,
drag_over_styles: SmallVec<[(TypeId, StyleRefinement); 2]>,
@@ -867,7 +880,9 @@ impl<V> Default for StatelessInteractivity<V> {
mouse_up_listeners: SmallVec::new(),
mouse_move_listeners: SmallVec::new(),
scroll_wheel_listeners: SmallVec::new(),
- key_listeners: SmallVec::new(),
+ key_down_listeners: SmallVec::new(),
+ key_up_listeners: SmallVec::new(),
+ action_listeners: SmallVec::new(),
hover_style: StyleRefinement::default(),
group_hover_style: None,
drag_over_styles: SmallVec::new(),
@@ -1202,16 +1217,14 @@ pub(crate) type HoverListener<V> = Box<dyn Fn(&mut V, bool, &mut ViewContext<V>)
pub(crate) type TooltipBuilder<V> = Arc<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyView + 'static>;
-pub type KeyListener<V> = Box<
- dyn Fn(
- &mut V,
- &dyn Any,
- &[&KeyContext],
- DispatchPhase,
- &mut ViewContext<V>,
- ) -> Option<Box<dyn Action>>
- + 'static,
->;
+pub(crate) type KeyDownListener<V> =
+ Box<dyn Fn(&mut V, &KeyDownEvent, DispatchPhase, &mut ViewContext<V>) + 'static>;
+
+pub(crate) type KeyUpListener<V> =
+ Box<dyn Fn(&mut V, &KeyUpEvent, DispatchPhase, &mut ViewContext<V>) + 'static>;
+
+pub type ActionListener<V> =
+ Box<dyn Fn(&mut V, &dyn Any, DispatchPhase, &mut ViewContext<V>) + 'static>;
#[cfg(test)]
mod test {