mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-08 07:35:01 +03:00
Merge pull request #2134 from zed-industries/fix-action-keystroke-bugs
Fix several action dispatching bugs
This commit is contained in:
commit
767d2f9766
@ -63,6 +63,7 @@ pub struct ContextMenu {
|
|||||||
visible: bool,
|
visible: bool,
|
||||||
previously_focused_view_id: Option<usize>,
|
previously_focused_view_id: Option<usize>,
|
||||||
clicked: bool,
|
clicked: bool,
|
||||||
|
parent_view_id: usize,
|
||||||
_actions_observation: Subscription,
|
_actions_observation: Subscription,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,6 +115,8 @@ impl View for ContextMenu {
|
|||||||
|
|
||||||
impl ContextMenu {
|
impl ContextMenu {
|
||||||
pub fn new(cx: &mut ViewContext<Self>) -> Self {
|
pub fn new(cx: &mut ViewContext<Self>) -> Self {
|
||||||
|
let parent_view_id = cx.parent().unwrap();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
show_count: 0,
|
show_count: 0,
|
||||||
anchor_position: Default::default(),
|
anchor_position: Default::default(),
|
||||||
@ -123,6 +126,7 @@ impl ContextMenu {
|
|||||||
visible: Default::default(),
|
visible: Default::default(),
|
||||||
previously_focused_view_id: Default::default(),
|
previously_focused_view_id: Default::default(),
|
||||||
clicked: false,
|
clicked: false,
|
||||||
|
parent_view_id,
|
||||||
_actions_observation: cx.observe_actions(Self::action_dispatched),
|
_actions_observation: cx.observe_actions(Self::action_dispatched),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -251,6 +255,7 @@ impl ContextMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn render_menu_for_measurement(&self, cx: &mut RenderContext<Self>) -> impl Element {
|
fn render_menu_for_measurement(&self, cx: &mut RenderContext<Self>) -> impl Element {
|
||||||
|
let window_id = cx.window_id();
|
||||||
let style = cx.global::<Settings>().theme.context_menu.clone();
|
let style = cx.global::<Settings>().theme.context_menu.clone();
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_child(
|
.with_child(
|
||||||
@ -289,6 +294,8 @@ impl ContextMenu {
|
|||||||
Some(ix) == self.selected_index,
|
Some(ix) == self.selected_index,
|
||||||
);
|
);
|
||||||
KeystrokeLabel::new(
|
KeystrokeLabel::new(
|
||||||
|
window_id,
|
||||||
|
self.parent_view_id,
|
||||||
action.boxed_clone(),
|
action.boxed_clone(),
|
||||||
style.keystroke.container,
|
style.keystroke.container,
|
||||||
style.keystroke.text.clone(),
|
style.keystroke.text.clone(),
|
||||||
@ -318,6 +325,7 @@ impl ContextMenu {
|
|||||||
|
|
||||||
let style = cx.global::<Settings>().theme.context_menu.clone();
|
let style = cx.global::<Settings>().theme.context_menu.clone();
|
||||||
|
|
||||||
|
let window_id = cx.window_id();
|
||||||
MouseEventHandler::<Menu>::new(0, cx, |_, cx| {
|
MouseEventHandler::<Menu>::new(0, cx, |_, cx| {
|
||||||
Flex::column()
|
Flex::column()
|
||||||
.with_children(self.items.iter().enumerate().map(|(ix, item)| {
|
.with_children(self.items.iter().enumerate().map(|(ix, item)| {
|
||||||
@ -337,6 +345,8 @@ impl ContextMenu {
|
|||||||
)
|
)
|
||||||
.with_child({
|
.with_child({
|
||||||
KeystrokeLabel::new(
|
KeystrokeLabel::new(
|
||||||
|
window_id,
|
||||||
|
self.parent_view_id,
|
||||||
action.boxed_clone(),
|
action.boxed_clone(),
|
||||||
style.keystroke.container,
|
style.keystroke.container,
|
||||||
style.keystroke.text.clone(),
|
style.keystroke.text.clone(),
|
||||||
|
@ -1332,6 +1332,31 @@ impl MutableAppContext {
|
|||||||
self.action_deserializers.keys().copied()
|
self.action_deserializers.keys().copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return keystrokes that would dispatch the given action on the given view.
|
||||||
|
pub(crate) fn keystrokes_for_action(
|
||||||
|
&mut self,
|
||||||
|
window_id: usize,
|
||||||
|
view_id: usize,
|
||||||
|
action: &dyn Action,
|
||||||
|
) -> Option<SmallVec<[Keystroke; 2]>> {
|
||||||
|
let mut contexts = Vec::new();
|
||||||
|
for view_id in self.ancestors(window_id, view_id) {
|
||||||
|
if let Some(view) = self.views.get(&(window_id, view_id)) {
|
||||||
|
contexts.push(view.keymap_context(self));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.keystroke_matcher
|
||||||
|
.bindings_for_action_type(action.as_any().type_id())
|
||||||
|
.find_map(|b| {
|
||||||
|
if b.match_context(&contexts) {
|
||||||
|
b.keystrokes().map(|s| s.into())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn available_actions(
|
pub fn available_actions(
|
||||||
&self,
|
&self,
|
||||||
window_id: usize,
|
window_id: usize,
|
||||||
@ -1339,8 +1364,10 @@ impl MutableAppContext {
|
|||||||
) -> impl Iterator<Item = (&'static str, Box<dyn Action>, SmallVec<[&Binding; 1]>)> {
|
) -> impl Iterator<Item = (&'static str, Box<dyn Action>, SmallVec<[&Binding; 1]>)> {
|
||||||
let mut action_types: HashSet<_> = self.global_actions.keys().copied().collect();
|
let mut action_types: HashSet<_> = self.global_actions.keys().copied().collect();
|
||||||
|
|
||||||
|
let mut contexts = Vec::new();
|
||||||
for view_id in self.ancestors(window_id, view_id) {
|
for view_id in self.ancestors(window_id, view_id) {
|
||||||
if let Some(view) = self.views.get(&(window_id, view_id)) {
|
if let Some(view) = self.views.get(&(window_id, view_id)) {
|
||||||
|
contexts.push(view.keymap_context(self));
|
||||||
let view_type = view.as_any().type_id();
|
let view_type = view.as_any().type_id();
|
||||||
if let Some(actions) = self.actions.get(&view_type) {
|
if let Some(actions) = self.actions.get(&view_type) {
|
||||||
action_types.extend(actions.keys().copied());
|
action_types.extend(actions.keys().copied());
|
||||||
@ -1357,6 +1384,7 @@ impl MutableAppContext {
|
|||||||
deserialize("{}").ok()?,
|
deserialize("{}").ok()?,
|
||||||
self.keystroke_matcher
|
self.keystroke_matcher
|
||||||
.bindings_for_action_type(*type_id)
|
.bindings_for_action_type(*type_id)
|
||||||
|
.filter(|b| b.match_context(&contexts))
|
||||||
.collect(),
|
.collect(),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
@ -1384,34 +1412,6 @@ impl MutableAppContext {
|
|||||||
self.global_actions.contains_key(&action_type)
|
self.global_actions.contains_key(&action_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return keystrokes that would dispatch the given action closest to the focused view, if there are any.
|
|
||||||
pub(crate) fn keystrokes_for_action(
|
|
||||||
&mut self,
|
|
||||||
window_id: usize,
|
|
||||||
view_stack: &[usize],
|
|
||||||
action: &dyn Action,
|
|
||||||
) -> Option<SmallVec<[Keystroke; 2]>> {
|
|
||||||
self.keystroke_matcher.contexts.clear();
|
|
||||||
for view_id in view_stack.iter().rev() {
|
|
||||||
let view = self
|
|
||||||
.cx
|
|
||||||
.views
|
|
||||||
.get(&(window_id, *view_id))
|
|
||||||
.expect("view in responder chain does not exist");
|
|
||||||
self.keystroke_matcher
|
|
||||||
.contexts
|
|
||||||
.push(view.keymap_context(self.as_ref()));
|
|
||||||
let keystrokes = self
|
|
||||||
.keystroke_matcher
|
|
||||||
.keystrokes_for_action(action, &self.keystroke_matcher.contexts);
|
|
||||||
if keystrokes.is_some() {
|
|
||||||
return keystrokes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
// Traverses the parent tree. Walks down the tree toward the passed
|
// Traverses the parent tree. Walks down the tree toward the passed
|
||||||
// view calling visit with true. Then walks back up the tree calling visit with false.
|
// view calling visit with true. Then walks back up the tree calling visit with false.
|
||||||
// If `visit` returns false this function will immediately return.
|
// If `visit` returns false this function will immediately return.
|
||||||
@ -1915,10 +1915,11 @@ impl MutableAppContext {
|
|||||||
{
|
{
|
||||||
self.update(|this| {
|
self.update(|this| {
|
||||||
let view_id = post_inc(&mut this.next_entity_id);
|
let view_id = post_inc(&mut this.next_entity_id);
|
||||||
|
// Make sure we can tell child views about their parent
|
||||||
|
this.cx.parents.insert((window_id, view_id), parent_id);
|
||||||
let mut cx = ViewContext::new(this, window_id, view_id);
|
let mut cx = ViewContext::new(this, window_id, view_id);
|
||||||
let handle = if let Some(view) = build_view(&mut cx) {
|
let handle = if let Some(view) = build_view(&mut cx) {
|
||||||
this.cx.views.insert((window_id, view_id), Box::new(view));
|
this.cx.views.insert((window_id, view_id), Box::new(view));
|
||||||
this.cx.parents.insert((window_id, view_id), parent_id);
|
|
||||||
if let Some(window) = this.cx.windows.get_mut(&window_id) {
|
if let Some(window) = this.cx.windows.get_mut(&window_id) {
|
||||||
window
|
window
|
||||||
.invalidation
|
.invalidation
|
||||||
@ -1928,6 +1929,7 @@ impl MutableAppContext {
|
|||||||
}
|
}
|
||||||
Some(ViewHandle::new(window_id, view_id, &this.cx.ref_counts))
|
Some(ViewHandle::new(window_id, view_id, &this.cx.ref_counts))
|
||||||
} else {
|
} else {
|
||||||
|
this.cx.parents.remove(&(window_id, view_id));
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
handle
|
handle
|
||||||
@ -2813,6 +2815,16 @@ impl AppContext {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the id of the parent of the given view, or none if the given
|
||||||
|
/// view is the root.
|
||||||
|
fn parent(&self, window_id: usize, view_id: usize) -> Option<usize> {
|
||||||
|
if let Some(ParentId::View(view_id)) = self.parents.get(&(window_id, view_id)) {
|
||||||
|
Some(*view_id)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_child_focused(&self, view: impl Into<AnyViewHandle>) -> bool {
|
pub fn is_child_focused(&self, view: impl Into<AnyViewHandle>) -> bool {
|
||||||
let view = view.into();
|
let view = view.into();
|
||||||
if let Some(focused_view_id) = self.focused_view_id(view.window_id) {
|
if let Some(focused_view_id) = self.focused_view_id(view.window_id) {
|
||||||
@ -3852,6 +3864,10 @@ impl<'a, T: View> ViewContext<'a, T> {
|
|||||||
.build_and_insert_view(self.window_id, ParentId::View(self.view_id), build_view)
|
.build_and_insert_view(self.window_id, ParentId::View(self.view_id), build_view)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parent(&mut self) -> Option<usize> {
|
||||||
|
self.cx.parent(self.window_id, self.view_id)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn reparent(&mut self, view_handle: impl Into<AnyViewHandle>) {
|
pub fn reparent(&mut self, view_handle: impl Into<AnyViewHandle>) {
|
||||||
let view_handle = view_handle.into();
|
let view_handle = view_handle.into();
|
||||||
if self.window_id != view_handle.window_id {
|
if self.window_id != view_handle.window_id {
|
||||||
|
@ -12,15 +12,21 @@ pub struct KeystrokeLabel {
|
|||||||
action: Box<dyn Action>,
|
action: Box<dyn Action>,
|
||||||
container_style: ContainerStyle,
|
container_style: ContainerStyle,
|
||||||
text_style: TextStyle,
|
text_style: TextStyle,
|
||||||
|
window_id: usize,
|
||||||
|
view_id: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeystrokeLabel {
|
impl KeystrokeLabel {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
window_id: usize,
|
||||||
|
view_id: usize,
|
||||||
action: Box<dyn Action>,
|
action: Box<dyn Action>,
|
||||||
container_style: ContainerStyle,
|
container_style: ContainerStyle,
|
||||||
text_style: TextStyle,
|
text_style: TextStyle,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
window_id,
|
||||||
|
view_id,
|
||||||
action,
|
action,
|
||||||
container_style,
|
container_style,
|
||||||
text_style,
|
text_style,
|
||||||
@ -37,7 +43,10 @@ impl Element for KeystrokeLabel {
|
|||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
cx: &mut LayoutContext,
|
cx: &mut LayoutContext,
|
||||||
) -> (Vector2F, ElementBox) {
|
) -> (Vector2F, ElementBox) {
|
||||||
let mut element = if let Some(keystrokes) = cx.keystrokes_for_action(self.action.as_ref()) {
|
let mut element = if let Some(keystrokes) =
|
||||||
|
cx.app
|
||||||
|
.keystrokes_for_action(self.window_id, self.view_id, self.action.as_ref())
|
||||||
|
{
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.with_children(keystrokes.iter().map(|keystroke| {
|
.with_children(keystrokes.iter().map(|keystroke| {
|
||||||
Label::new(keystroke.to_string(), self.text_style.clone())
|
Label::new(keystroke.to_string(), self.text_style.clone())
|
||||||
|
@ -61,11 +61,14 @@ impl Tooltip {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
struct ElementState<Tag>(Tag);
|
struct ElementState<Tag>(Tag);
|
||||||
struct MouseEventHandlerState<Tag>(Tag);
|
struct MouseEventHandlerState<Tag>(Tag);
|
||||||
|
let focused_view_id = cx.focused_view_id(cx.window_id).unwrap();
|
||||||
|
|
||||||
let state_handle = cx.default_element_state::<ElementState<Tag>, Rc<TooltipState>>(id);
|
let state_handle = cx.default_element_state::<ElementState<Tag>, Rc<TooltipState>>(id);
|
||||||
let state = state_handle.read(cx).clone();
|
let state = state_handle.read(cx).clone();
|
||||||
let tooltip = if state.visible.get() {
|
let tooltip = if state.visible.get() {
|
||||||
let mut collapsed_tooltip = Self::render_tooltip(
|
let mut collapsed_tooltip = Self::render_tooltip(
|
||||||
|
cx.window_id,
|
||||||
|
focused_view_id,
|
||||||
text.clone(),
|
text.clone(),
|
||||||
style.clone(),
|
style.clone(),
|
||||||
action.as_ref().map(|a| a.boxed_clone()),
|
action.as_ref().map(|a| a.boxed_clone()),
|
||||||
@ -74,7 +77,7 @@ impl Tooltip {
|
|||||||
.boxed();
|
.boxed();
|
||||||
Some(
|
Some(
|
||||||
Overlay::new(
|
Overlay::new(
|
||||||
Self::render_tooltip(text, style, action, false)
|
Self::render_tooltip(cx.window_id, focused_view_id, text, style, action, false)
|
||||||
.constrained()
|
.constrained()
|
||||||
.dynamically(move |constraint, cx| {
|
.dynamically(move |constraint, cx| {
|
||||||
SizeConstraint::strict_along(
|
SizeConstraint::strict_along(
|
||||||
@ -128,6 +131,8 @@ impl Tooltip {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_tooltip(
|
pub fn render_tooltip(
|
||||||
|
window_id: usize,
|
||||||
|
focused_view_id: usize,
|
||||||
text: String,
|
text: String,
|
||||||
style: TooltipStyle,
|
style: TooltipStyle,
|
||||||
action: Option<Box<dyn Action>>,
|
action: Option<Box<dyn Action>>,
|
||||||
@ -145,8 +150,13 @@ impl Tooltip {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.with_children(action.map(|action| {
|
.with_children(action.map(|action| {
|
||||||
let keystroke_label =
|
let keystroke_label = KeystrokeLabel::new(
|
||||||
KeystrokeLabel::new(action, style.keystroke.container, style.keystroke.text);
|
window_id,
|
||||||
|
focused_view_id,
|
||||||
|
action,
|
||||||
|
style.keystroke.container,
|
||||||
|
style.keystroke.text,
|
||||||
|
);
|
||||||
if measure {
|
if measure {
|
||||||
keystroke_label.boxed()
|
keystroke_label.boxed()
|
||||||
} else {
|
} else {
|
||||||
|
@ -41,7 +41,7 @@ impl Binding {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_context(&self, contexts: &[KeymapContext]) -> bool {
|
pub fn match_context(&self, contexts: &[KeymapContext]) -> bool {
|
||||||
self.context_predicate
|
self.context_predicate
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|predicate| predicate.eval(contexts))
|
.map(|predicate| predicate.eval(contexts))
|
||||||
|
@ -43,7 +43,7 @@ impl KeymapContextPredicate {
|
|||||||
pub fn eval(&self, contexts: &[KeymapContext]) -> bool {
|
pub fn eval(&self, contexts: &[KeymapContext]) -> bool {
|
||||||
let Some(context) = contexts.first() else { return false };
|
let Some(context) = contexts.first() else { return false };
|
||||||
match self {
|
match self {
|
||||||
Self::Identifier(name) => context.set.contains(name.as_str()),
|
Self::Identifier(name) => (&context.set).contains(name.as_str()),
|
||||||
Self::Equal(left, right) => context
|
Self::Equal(left, right) => context
|
||||||
.map
|
.map
|
||||||
.get(left)
|
.get(left)
|
||||||
|
@ -14,12 +14,12 @@ use pathfinder_geometry::{
|
|||||||
|
|
||||||
pub trait Vector2FExt {
|
pub trait Vector2FExt {
|
||||||
/// Converts self to an NSPoint with y axis pointing up.
|
/// Converts self to an NSPoint with y axis pointing up.
|
||||||
fn to_screen_ns_point(&self, native_window: id) -> NSPoint;
|
fn to_screen_ns_point(&self, native_window: id, window_height: f64) -> NSPoint;
|
||||||
}
|
}
|
||||||
impl Vector2FExt for Vector2F {
|
impl Vector2FExt for Vector2F {
|
||||||
fn to_screen_ns_point(&self, native_window: id) -> NSPoint {
|
fn to_screen_ns_point(&self, native_window: id, window_height: f64) -> NSPoint {
|
||||||
unsafe {
|
unsafe {
|
||||||
let point = NSPoint::new(self.x() as f64, -self.y() as f64);
|
let point = NSPoint::new(self.x() as f64, window_height - self.y() as f64);
|
||||||
msg_send![native_window, convertPointToScreen: point]
|
msg_send![native_window, convertPointToScreen: point]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -483,6 +483,7 @@ impl Window {
|
|||||||
|
|
||||||
let native_view: id = msg_send![VIEW_CLASS, alloc];
|
let native_view: id = msg_send![VIEW_CLASS, alloc];
|
||||||
let native_view = NSView::init(native_view);
|
let native_view = NSView::init(native_view);
|
||||||
|
|
||||||
assert!(!native_view.is_null());
|
assert!(!native_view.is_null());
|
||||||
|
|
||||||
let window = Self(Rc::new(RefCell::new(WindowState {
|
let window = Self(Rc::new(RefCell::new(WindowState {
|
||||||
@ -828,12 +829,14 @@ impl platform::Window for Window {
|
|||||||
let self_id = self_borrow.id;
|
let self_id = self_borrow.id;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let window_frame = self_borrow.frame();
|
|
||||||
let app = NSApplication::sharedApplication(nil);
|
let app = NSApplication::sharedApplication(nil);
|
||||||
|
|
||||||
// Convert back to screen coordinates
|
// Convert back to screen coordinates
|
||||||
let screen_point =
|
let screen_point = position.to_screen_ns_point(
|
||||||
(position + window_frame.origin()).to_screen_ns_point(self_borrow.native_window);
|
self_borrow.native_window,
|
||||||
|
self_borrow.content_size().y() as f64,
|
||||||
|
);
|
||||||
|
|
||||||
let window_number: NSInteger = msg_send![class!(NSWindow), windowNumberAtPoint:screen_point belowWindowWithWindowNumber:0];
|
let window_number: NSInteger = msg_send![class!(NSWindow), windowNumberAtPoint:screen_point belowWindowWithWindowNumber:0];
|
||||||
let top_most_window: id = msg_send![app, windowWithWindowNumber: window_number];
|
let top_most_window: id = msg_send![app, windowWithWindowNumber: window_number];
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ use crate::{
|
|||||||
font_cache::FontCache,
|
font_cache::FontCache,
|
||||||
geometry::rect::RectF,
|
geometry::rect::RectF,
|
||||||
json::{self, ToJson},
|
json::{self, ToJson},
|
||||||
keymap_matcher::Keystroke,
|
|
||||||
platform::{CursorStyle, Event},
|
platform::{CursorStyle, Event},
|
||||||
scene::{
|
scene::{
|
||||||
CursorRegion, MouseClick, MouseDown, MouseDownOut, MouseDrag, MouseEvent, MouseHover,
|
CursorRegion, MouseClick, MouseDown, MouseDownOut, MouseDrag, MouseEvent, MouseHover,
|
||||||
@ -604,14 +603,6 @@ pub struct LayoutContext<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> LayoutContext<'a> {
|
impl<'a> LayoutContext<'a> {
|
||||||
pub(crate) fn keystrokes_for_action(
|
|
||||||
&mut self,
|
|
||||||
action: &dyn Action,
|
|
||||||
) -> Option<SmallVec<[Keystroke; 2]>> {
|
|
||||||
self.app
|
|
||||||
.keystrokes_for_action(self.window_id, &self.view_stack, action)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn layout(&mut self, view_id: usize, constraint: SizeConstraint) -> Vector2F {
|
fn layout(&mut self, view_id: usize, constraint: SizeConstraint) -> Vector2F {
|
||||||
let print_error = |view_id| {
|
let print_error = |view_id| {
|
||||||
format!(
|
format!(
|
||||||
|
Loading…
Reference in New Issue
Block a user