diff --git a/crates/gpui/src/elements/event_handler.rs b/crates/gpui/src/elements/event_handler.rs index 0eea82fa02..2132939c1c 100644 --- a/crates/gpui/src/elements/event_handler.rs +++ b/crates/gpui/src/elements/event_handler.rs @@ -10,6 +10,8 @@ pub struct EventHandler { child: ElementBox, capture: Option bool>>, mouse_down: Option bool>>, + right_mouse_down: Option bool>>, + other_mouse_down: Option bool>>, } impl EventHandler { @@ -18,6 +20,8 @@ impl EventHandler { child, capture: None, mouse_down: None, + right_mouse_down: None, + other_mouse_down: None, } } @@ -29,6 +33,22 @@ impl EventHandler { self } + pub fn on_right_mouse_down(mut self, callback: F) -> Self + where + F: 'static + FnMut(&mut EventContext) -> bool, + { + self.right_mouse_down = Some(Box::new(callback)); + self + } + + pub fn on_other_mouse_down(mut self, callback: F) -> Self + where + F: 'static + FnMut(u16, &mut EventContext) -> bool, + { + self.other_mouse_down = Some(Box::new(callback)); + self + } + pub fn capture(mut self, callback: F) -> Self where F: 'static + FnMut(&Event, RectF, &mut EventContext) -> bool, @@ -86,7 +106,23 @@ impl Element for EventHandler { } } false - } + }, + Event::RightMouseDown { position, .. } => { + if let Some(callback) = self.right_mouse_down.as_mut() { + if bounds.contains_point(*position) { + return callback(cx); + } + } + false + }, + Event::OtherMouseDown { position, button, .. } => { + if let Some(callback) = self.other_mouse_down.as_mut() { + if bounds.contains_point(*position) { + return callback(*button, cx); + } + } + false + }, _ => false, } } diff --git a/crates/gpui/src/platform/event.rs b/crates/gpui/src/platform/event.rs index 98762b306a..19b8eec189 100644 --- a/crates/gpui/src/platform/event.rs +++ b/crates/gpui/src/platform/event.rs @@ -26,6 +26,30 @@ pub enum Event { LeftMouseDragged { position: Vector2F, }, + RightMouseDown { + position: Vector2F, + ctrl: bool, + alt: bool, + shift: bool, + cmd: bool, + click_count: usize, + }, + RightMouseUp { + position: Vector2F, + }, + OtherMouseDown { + position: Vector2F, + button: u16, + ctrl: bool, + alt: bool, + shift: bool, + cmd: bool, + click_count: usize, + }, + OtherMouseUp { + position: Vector2F, + button: u16, + }, MouseMoved { position: Vector2F, left_mouse_down: bool, diff --git a/crates/gpui/src/platform/mac/event.rs b/crates/gpui/src/platform/mac/event.rs index 7c870e4720..4b1f9d3a38 100644 --- a/crates/gpui/src/platform/mac/event.rs +++ b/crates/gpui/src/platform/mac/event.rs @@ -125,6 +125,48 @@ impl Event { window_height - native_event.locationInWindow().y as f32, ), }), + NSEventType::NSRightMouseDown => { + let modifiers = native_event.modifierFlags(); + window_height.map(|window_height| Self::RightMouseDown { + position: vec2f( + native_event.locationInWindow().x as f32, + window_height - native_event.locationInWindow().y as f32, + ), + ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask), + alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask), + shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask), + cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask), + click_count: native_event.clickCount() as usize, + }) + } + NSEventType::NSRightMouseUp => window_height.map(|window_height| Self::RightMouseUp { + position: vec2f( + native_event.locationInWindow().x as f32, + window_height - native_event.locationInWindow().y as f32, + ), + }), + NSEventType::NSOtherMouseDown => { + let modifiers = native_event.modifierFlags(); + window_height.map(|window_height| Self::OtherMouseDown { + position: vec2f( + native_event.locationInWindow().x as f32, + window_height - native_event.locationInWindow().y as f32, + ), + button: native_event.buttonNumber() as u16, + ctrl: modifiers.contains(NSEventModifierFlags::NSControlKeyMask), + alt: modifiers.contains(NSEventModifierFlags::NSAlternateKeyMask), + shift: modifiers.contains(NSEventModifierFlags::NSShiftKeyMask), + cmd: modifiers.contains(NSEventModifierFlags::NSCommandKeyMask), + click_count: native_event.clickCount() as usize, + }) + } + NSEventType::NSOtherMouseUp => window_height.map(|window_height| Self::OtherMouseUp { + position: vec2f( + native_event.locationInWindow().x as f32, + window_height - native_event.locationInWindow().y as f32, + ), + button: native_event.buttonNumber() as u16, + }), NSEventType::NSLeftMouseDragged => { window_height.map(|window_height| Self::LeftMouseDragged { position: vec2f( diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index 9fd34166ff..5281009155 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -95,6 +95,22 @@ unsafe fn build_classes() { sel!(mouseUp:), handle_view_event as extern "C" fn(&Object, Sel, id), ); + decl.add_method( + sel!(rightMouseDown:), + handle_view_event as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(rightMouseUp:), + handle_view_event as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(otherMouseDown:), + handle_view_event as extern "C" fn(&Object, Sel, id), + ); + decl.add_method( + sel!(otherMouseUp:), + handle_view_event as extern "C" fn(&Object, Sel, id), + ); decl.add_method( sel!(mouseMoved:), handle_view_event as extern "C" fn(&Object, Sel, id), diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 78f84d7da2..02a7c03c90 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -8,7 +8,7 @@ use gpui::{ keymap::Binding, platform::CursorStyle, AnyViewHandle, Entity, MutableAppContext, Quad, RenderContext, Task, View, ViewContext, - ViewHandle, + ViewHandle, WeakViewHandle, }; use postage::watch; use project::ProjectPath; @@ -27,8 +27,8 @@ action!(ActivateNextItem); action!(CloseActiveItem); action!(CloseInactiveItems); action!(CloseItem, usize); -action!(GoBack); -action!(GoForward); +action!(GoBack, Option>); +action!(GoForward, Option>); const MAX_NAVIGATION_HISTORY_LEN: usize = 1024; @@ -54,11 +54,19 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_action(|pane: &mut Pane, action: &Split, cx| { pane.split(action.0, cx); }); - cx.add_action(|workspace: &mut Workspace, _: &GoBack, cx| { - Pane::go_back(workspace, cx).detach(); + cx.add_action(|workspace: &mut Workspace, action: &GoBack, cx| { + Pane::go_back( + workspace, + action.0.as_ref().and_then(|weak_handle| weak_handle.upgrade(cx)), + cx + ).detach(); }); - cx.add_action(|workspace: &mut Workspace, _: &GoForward, cx| { - Pane::go_forward(workspace, cx).detach(); + cx.add_action(|workspace: &mut Workspace, action: &GoForward, cx| { + Pane::go_forward( + workspace, + action.0.as_ref().and_then(|weak_handle| weak_handle.upgrade(cx)), + cx + ).detach(); }); cx.add_bindings(vec![ @@ -70,8 +78,8 @@ pub fn init(cx: &mut MutableAppContext) { Binding::new("cmd-k down", Split(SplitDirection::Down), Some("Pane")), Binding::new("cmd-k left", Split(SplitDirection::Left), Some("Pane")), Binding::new("cmd-k right", Split(SplitDirection::Right), Some("Pane")), - Binding::new("ctrl--", GoBack, Some("Pane")), - Binding::new("shift-ctrl-_", GoForward, Some("Pane")), + Binding::new("ctrl--", GoBack(None), Some("Pane")), + Binding::new("shift-ctrl-_", GoForward(None), Some("Pane")), ]); } @@ -163,19 +171,19 @@ impl Pane { cx.emit(Event::Activate); } - pub fn go_back(workspace: &mut Workspace, cx: &mut ViewContext) -> Task<()> { + pub fn go_back(workspace: &mut Workspace, pane: Option>, cx: &mut ViewContext) -> Task<()> { Self::navigate_history( workspace, - workspace.active_pane().clone(), + pane.unwrap_or_else(|| workspace.active_pane().clone()), NavigationMode::GoingBack, cx, ) } - pub fn go_forward(workspace: &mut Workspace, cx: &mut ViewContext) -> Task<()> { + pub fn go_forward(workspace: &mut Workspace, pane: Option>, cx: &mut ViewContext) -> Task<()> { Self::navigate_history( workspace, - workspace.active_pane().clone(), + pane.unwrap_or_else(|| workspace.active_pane().clone()), NavigationMode::GoingForward, cx, ) @@ -187,6 +195,8 @@ impl Pane { mode: NavigationMode, cx: &mut ViewContext, ) -> Task<()> { + workspace.activate_pane(pane.clone(), cx); + let to_load = pane.update(cx, |pane, cx| { // Retrieve the weak item handle from the history. let entry = pane.nav_history.borrow_mut().pop(mode)?; @@ -634,19 +644,33 @@ impl View for Pane { } fn render(&mut self, cx: &mut RenderContext) -> ElementBox { - if let Some(active_item) = self.active_item() { - Flex::column() - .with_child(self.render_tabs(cx)) - .with_children( - self.active_toolbar() - .as_ref() - .map(|view| ChildView::new(view).boxed()), - ) - .with_child(ChildView::new(active_item).flexible(1., true).boxed()) - .named("pane") - } else { - Empty::new().named("pane") - } + let this = cx.handle(); + + EventHandler::new( + if let Some(active_item) = self.active_item() { + Flex::column() + .with_child(self.render_tabs(cx)) + .with_children( + self.active_toolbar() + .as_ref() + .map(|view| ChildView::new(view).boxed()), + ) + .with_child(ChildView::new(active_item).flexible(1., true).boxed()) + .boxed() + } else { + Empty::new().boxed() + } + ) + .on_other_mouse_down(move |button, cx| { + match button { + 3 => cx.dispatch_action(GoBack(Some(this.clone()))), + 4 => cx.dispatch_action(GoForward(Some(this.clone()))), + _ => return false, + }; + true + }) + .named("pane") + } fn on_focus(&mut self, cx: &mut ViewContext) { diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index c9a14bf236..75e7735f24 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -747,44 +747,44 @@ mod tests { (file3.clone(), DisplayPoint::new(15, 0)) ); - workspace.update(cx, |w, cx| Pane::go_back(w, cx)).await; + workspace.update(cx, |w, cx| Pane::go_back(w, None, cx)).await; assert_eq!( active_location(&workspace, cx), (file3.clone(), DisplayPoint::new(0, 0)) ); - workspace.update(cx, |w, cx| Pane::go_back(w, cx)).await; + workspace.update(cx, |w, cx| Pane::go_back(w, None, cx)).await; assert_eq!( active_location(&workspace, cx), (file2.clone(), DisplayPoint::new(0, 0)) ); - workspace.update(cx, |w, cx| Pane::go_back(w, cx)).await; + workspace.update(cx, |w, cx| Pane::go_back(w, None, cx)).await; assert_eq!( active_location(&workspace, cx), (file1.clone(), DisplayPoint::new(10, 0)) ); - workspace.update(cx, |w, cx| Pane::go_back(w, cx)).await; + workspace.update(cx, |w, cx| Pane::go_back(w, None, cx)).await; assert_eq!( active_location(&workspace, cx), (file1.clone(), DisplayPoint::new(0, 0)) ); // Go back one more time and ensure we don't navigate past the first item in the history. - workspace.update(cx, |w, cx| Pane::go_back(w, cx)).await; + workspace.update(cx, |w, cx| Pane::go_back(w, None, cx)).await; assert_eq!( active_location(&workspace, cx), (file1.clone(), DisplayPoint::new(0, 0)) ); - workspace.update(cx, |w, cx| Pane::go_forward(w, cx)).await; + workspace.update(cx, |w, cx| Pane::go_forward(w, None, cx)).await; assert_eq!( active_location(&workspace, cx), (file1.clone(), DisplayPoint::new(10, 0)) ); - workspace.update(cx, |w, cx| Pane::go_forward(w, cx)).await; + workspace.update(cx, |w, cx| Pane::go_forward(w, None, cx)).await; assert_eq!( active_location(&workspace, cx), (file2.clone(), DisplayPoint::new(0, 0)) @@ -798,7 +798,7 @@ mod tests { .update(cx, |pane, cx| pane.close_item(editor3.id(), cx)); drop(editor3); }); - workspace.update(cx, |w, cx| Pane::go_forward(w, cx)).await; + workspace.update(cx, |w, cx| Pane::go_forward(w, None, cx)).await; assert_eq!( active_location(&workspace, cx), (file3.clone(), DisplayPoint::new(0, 0)) @@ -818,12 +818,12 @@ mod tests { }) .await .unwrap(); - workspace.update(cx, |w, cx| Pane::go_back(w, cx)).await; + workspace.update(cx, |w, cx| Pane::go_back(w, None, cx)).await; assert_eq!( active_location(&workspace, cx), (file1.clone(), DisplayPoint::new(10, 0)) ); - workspace.update(cx, |w, cx| Pane::go_forward(w, cx)).await; + workspace.update(cx, |w, cx| Pane::go_forward(w, None, cx)).await; assert_eq!( active_location(&workspace, cx), (file3.clone(), DisplayPoint::new(0, 0))