Detailed changes
@@ -3,13 +3,15 @@ use serde_json::json;
use crate::{
geometry::vector::Vector2F, DebugContext, Element, ElementBox, Event, EventContext,
- LayoutContext, PaintContext, SizeConstraint,
+ LayoutContext, NavigationDirection, PaintContext, SizeConstraint,
};
pub struct EventHandler {
child: ElementBox,
capture: Option<Box<dyn FnMut(&Event, RectF, &mut EventContext) -> bool>>,
mouse_down: Option<Box<dyn FnMut(&mut EventContext) -> bool>>,
+ right_mouse_down: Option<Box<dyn FnMut(&mut EventContext) -> bool>>,
+ navigate_mouse_down: Option<Box<dyn FnMut(NavigationDirection, &mut EventContext) -> bool>>,
}
impl EventHandler {
@@ -18,6 +20,8 @@ impl EventHandler {
child,
capture: None,
mouse_down: None,
+ right_mouse_down: None,
+ navigate_mouse_down: None,
}
}
@@ -29,6 +33,22 @@ impl EventHandler {
self
}
+ pub fn on_right_mouse_down<F>(mut self, callback: F) -> Self
+ where
+ F: 'static + FnMut(&mut EventContext) -> bool,
+ {
+ self.right_mouse_down = Some(Box::new(callback));
+ self
+ }
+
+ pub fn on_navigate_mouse_down<F>(mut self, callback: F) -> Self
+ where
+ F: 'static + FnMut(NavigationDirection, &mut EventContext) -> bool,
+ {
+ self.navigate_mouse_down = Some(Box::new(callback));
+ self
+ }
+
pub fn capture<F>(mut self, callback: F) -> Self
where
F: 'static + FnMut(&Event, RectF, &mut EventContext) -> bool,
@@ -87,6 +107,26 @@ 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::NavigateMouseDown {
+ position,
+ direction,
+ ..
+ } => {
+ if let Some(callback) = self.navigate_mouse_down.as_mut() {
+ if bounds.contains_point(*position) {
+ return callback(*direction, cx);
+ }
+ }
+ false
+ }
_ => false,
}
}
@@ -29,7 +29,7 @@ pub mod keymap;
pub mod platform;
pub use gpui_macros::test;
pub use platform::FontSystem;
-pub use platform::{Event, PathPromptOptions, Platform, PromptLevel};
+pub use platform::{Event, NavigationDirection, PathPromptOptions, Platform, PromptLevel};
pub use presenter::{
Axis, DebugContext, EventContext, LayoutContext, PaintContext, SizeConstraint, Vector2FExt,
};
@@ -19,7 +19,7 @@ use crate::{
};
use anyhow::Result;
use async_task::Runnable;
-pub use event::Event;
+pub use event::{Event, NavigationDirection};
use postage::oneshot;
use std::{
any::Any,
@@ -1,5 +1,11 @@
use crate::{geometry::vector::Vector2F, keymap::Keystroke};
+#[derive(Copy, Clone, Debug)]
+pub enum NavigationDirection {
+ Back,
+ Forward,
+}
+
#[derive(Clone, Debug)]
pub enum Event {
KeyDown {
@@ -26,6 +32,30 @@ pub enum Event {
LeftMouseDragged {
position: Vector2F,
},
+ RightMouseDown {
+ position: Vector2F,
+ ctrl: bool,
+ alt: bool,
+ shift: bool,
+ cmd: bool,
+ click_count: usize,
+ },
+ RightMouseUp {
+ position: Vector2F,
+ },
+ NavigateMouseDown {
+ position: Vector2F,
+ direction: NavigationDirection,
+ ctrl: bool,
+ alt: bool,
+ shift: bool,
+ cmd: bool,
+ click_count: usize,
+ },
+ NavigateMouseUp {
+ position: Vector2F,
+ direction: NavigationDirection,
+ },
MouseMoved {
position: Vector2F,
left_mouse_down: bool,
@@ -1,4 +1,8 @@
-use crate::{geometry::vector::vec2f, keymap::Keystroke, platform::Event};
+use crate::{
+ geometry::vector::vec2f,
+ keymap::Keystroke,
+ platform::{Event, NavigationDirection},
+};
use cocoa::{
appkit::{NSEvent, NSEventModifierFlags, NSEventType},
base::{id, nil, YES},
@@ -125,6 +129,64 @@ 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 direction = match native_event.buttonNumber() {
+ 3 => NavigationDirection::Back,
+ 4 => NavigationDirection::Forward,
+ // Other mouse buttons aren't tracked currently
+ _ => return None,
+ };
+
+ let modifiers = native_event.modifierFlags();
+ window_height.map(|window_height| Self::NavigateMouseDown {
+ position: vec2f(
+ native_event.locationInWindow().x as f32,
+ window_height - native_event.locationInWindow().y as f32,
+ ),
+ direction,
+ 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 => {
+ let direction = match native_event.buttonNumber() {
+ 3 => NavigationDirection::Back,
+ 4 => NavigationDirection::Forward,
+ // Other mouse buttons aren't tracked currently
+ _ => return None,
+ };
+
+ window_height.map(|window_height| Self::NavigateMouseUp {
+ position: vec2f(
+ native_event.locationInWindow().x as f32,
+ window_height - native_event.locationInWindow().y as f32,
+ ),
+ direction,
+ })
+ }
NSEventType::NSLeftMouseDragged => {
window_height.map(|window_height| Self::LeftMouseDragged {
position: vec2f(
@@ -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),
@@ -6,9 +6,9 @@ use gpui::{
elements::*,
geometry::{rect::RectF, vector::vec2f},
keymap::Binding,
- platform::CursorStyle,
+ platform::{CursorStyle, NavigationDirection},
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<WeakViewHandle<Pane>>);
+action!(GoForward, Option<WeakViewHandle<Pane>>);
const MAX_NAVIGATION_HISTORY_LEN: usize = 1024;
@@ -54,11 +54,27 @@ 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 +86,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 +179,27 @@ impl Pane {
cx.emit(Event::Activate);
}
- pub fn go_back(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) -> Task<()> {
+ pub fn go_back(
+ workspace: &mut Workspace,
+ pane: Option<ViewHandle<Pane>>,
+ cx: &mut ViewContext<Workspace>,
+ ) -> 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<Workspace>) -> Task<()> {
+ pub fn go_forward(
+ workspace: &mut Workspace,
+ pane: Option<ViewHandle<Pane>>,
+ cx: &mut ViewContext<Workspace>,
+ ) -> Task<()> {
Self::navigate_history(
workspace,
- workspace.active_pane().clone(),
+ pane.unwrap_or_else(|| workspace.active_pane().clone()),
NavigationMode::GoingForward,
cx,
)
@@ -187,6 +211,8 @@ impl Pane {
mode: NavigationMode,
cx: &mut ViewContext<Workspace>,
) -> 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,7 +660,9 @@ impl View for Pane {
}
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
- if let Some(active_item) = self.active_item() {
+ let this = cx.handle();
+
+ EventHandler::new(if let Some(active_item) = self.active_item() {
Flex::column()
.with_child(self.render_tabs(cx))
.with_children(
@@ -643,10 +671,20 @@ impl View for Pane {
.map(|view| ChildView::new(view).boxed()),
)
.with_child(ChildView::new(active_item).flexible(1., true).boxed())
- .named("pane")
+ .boxed()
} else {
- Empty::new().named("pane")
- }
+ Empty::new().boxed()
+ })
+ .on_navigate_mouse_down(move |direction, cx| {
+ let this = this.clone();
+ match direction {
+ NavigationDirection::Back => cx.dispatch_action(GoBack(Some(this))),
+ NavigationDirection::Forward => cx.dispatch_action(GoForward(Some(this))),
+ }
+
+ true
+ })
+ .named("pane")
}
fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
@@ -747,44 +747,58 @@ 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 +812,9 @@ 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 +834,16 @@ 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))