From bd2252ea559db6d95a9eae12913ff4d2e8472c78 Mon Sep 17 00:00:00 2001 From: Ben Gotow Date: Thu, 21 Jan 2016 09:40:21 -0800 Subject: [PATCH 1/4] Extend NativeWindow to track touch-based scroll events on OS X In N1, we want to implement the famous "swipe to archive" action on threads in the user's inbox. Chrome exposes `scroll` and `wheel` events, but these aren't sufficient to implement the interaction because the element needs to "snap" when the user lifts their fingers from the trackpad, not when they / we stop receiving `wheel` / `scroll` events. These events may stop before the user lifts their fingers, or continue after the user has lifted their fingers if they had enough momentum for the gesture to continue. This exposes BrowserWindow `scroll-touch-down` and `scroll-touch-up`, which fire immeditaely when the user touches two fingers to the trackpad, and again when the user lifts their fingers. Combined with the existing wheel event should allow for "swipe-to-archive" and other similar interactions. Note: This is only implemented on Mac OS X and the events don't fire unless you're using a trackpad! Related: #1486, #2683, https://github.com/nylas/N1/issues/541 --- atom/browser/api/atom_api_window.cc | 8 ++++++++ atom/browser/api/atom_api_window.h | 2 ++ atom/browser/native_window.cc | 10 ++++++++++ atom/browser/native_window.h | 2 ++ atom/browser/native_window_mac.mm | 19 +++++++++++++++++++ atom/browser/native_window_observer.h | 2 ++ 6 files changed, 43 insertions(+) diff --git a/atom/browser/api/atom_api_window.cc b/atom/browser/api/atom_api_window.cc index 09d88edb9b7b..ef38490d8f7b 100644 --- a/atom/browser/api/atom_api_window.cc +++ b/atom/browser/api/atom_api_window.cc @@ -227,6 +227,14 @@ void Window::OnWindowLeaveFullScreen() { Emit("leave-full-screen"); } +void Window::OnWindowScrollTouchUp() { + Emit("scroll-touch-up"); +} + +void Window::OnWindowScrollTouchDown() { + Emit("scroll-touch-down"); +} + void Window::OnWindowEnterHtmlFullScreen() { Emit("enter-html-full-screen"); } diff --git a/atom/browser/api/atom_api_window.h b/atom/browser/api/atom_api_window.h index d8e25965d898..12eec04680f5 100644 --- a/atom/browser/api/atom_api_window.h +++ b/atom/browser/api/atom_api_window.h @@ -65,6 +65,8 @@ class Window : public mate::TrackableObject, void OnWindowResize() override; void OnWindowMove() override; void OnWindowMoved() override; + void OnWindowScrollTouchUp() override; + void OnWindowScrollTouchDown() override; void OnWindowEnterFullScreen() override; void OnWindowLeaveFullScreen() override; void OnWindowEnterHtmlFullScreen() override; diff --git a/atom/browser/native_window.cc b/atom/browser/native_window.cc index 4df4b4916963..89354381bead 100644 --- a/atom/browser/native_window.cc +++ b/atom/browser/native_window.cc @@ -442,6 +442,16 @@ void NativeWindow::NotifyWindowEnterFullScreen() { OnWindowEnterFullScreen()); } +void NativeWindow::NotifyWindowScrollTouchUp() { + FOR_EACH_OBSERVER(NativeWindowObserver, observers_, + OnWindowScrollTouchUp()); +} + +void NativeWindow::NotifyWindowScrollTouchDown() { + FOR_EACH_OBSERVER(NativeWindowObserver, observers_, + OnWindowScrollTouchDown()); +} + void NativeWindow::NotifyWindowLeaveFullScreen() { FOR_EACH_OBSERVER(NativeWindowObserver, observers_, OnWindowLeaveFullScreen()); diff --git a/atom/browser/native_window.h b/atom/browser/native_window.h index 0ccf3dfbc2ec..f1dac26650c7 100644 --- a/atom/browser/native_window.h +++ b/atom/browser/native_window.h @@ -205,6 +205,8 @@ class NativeWindow : public base::SupportsUserData, void NotifyWindowMove(); void NotifyWindowResize(); void NotifyWindowMoved(); + void NotifyWindowScrollTouchUp(); + void NotifyWindowScrollTouchDown(); void NotifyWindowEnterFullScreen(); void NotifyWindowLeaveFullScreen(); void NotifyWindowEnterHtmlFullScreen(); diff --git a/atom/browser/native_window_mac.mm b/atom/browser/native_window_mac.mm index f128f3b2cb71..198a78e3f594 100644 --- a/atom/browser/native_window_mac.mm +++ b/atom/browser/native_window_mac.mm @@ -490,6 +490,25 @@ NativeWindowMac::NativeWindowMac( NSView* view = inspectable_web_contents()->GetView()->GetNativeView(); [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + BOOL __block down = NO; + [NSEvent addLocalMonitorForEventsMatchingMask:NSScrollWheelMask handler:^NSEvent * _Nullable(NSEvent * event) { + if (![window_ isKeyWindow]) + return event; + + if (!web_contents) + return event; + + if (!down && (([event phase] == NSEventPhaseMayBegin) || ([event phase] == NSEventPhaseBegan))) { + this->NotifyWindowScrollTouchDown(); + down = YES; + } + if (down && (([event phase] == NSEventPhaseEnded) || ([event phase] == NSEventPhaseCancelled))) { + this->NotifyWindowScrollTouchUp(); + down = NO; + } + return event; + }]; + InstallView(); } diff --git a/atom/browser/native_window_observer.h b/atom/browser/native_window_observer.h index ce2d41c6fad6..b7ba9d756f11 100644 --- a/atom/browser/native_window_observer.h +++ b/atom/browser/native_window_observer.h @@ -50,6 +50,8 @@ class NativeWindowObserver { virtual void OnWindowResize() {} virtual void OnWindowMove() {} virtual void OnWindowMoved() {} + virtual void OnWindowScrollTouchUp() {} + virtual void OnWindowScrollTouchDown() {} virtual void OnWindowEnterFullScreen() {} virtual void OnWindowLeaveFullScreen() {} virtual void OnWindowEnterHtmlFullScreen() {} From d492ff45d5eb03a9eaf4826661933a275fdb9db3 Mon Sep 17 00:00:00 2001 From: Ben Gotow Date: Thu, 21 Jan 2016 16:31:09 -0800 Subject: [PATCH 2/4] Rename up/down => begin/end --- atom/browser/api/atom_api_window.cc | 8 ++++---- atom/browser/api/atom_api_window.h | 4 ++-- atom/browser/native_window.cc | 8 ++++---- atom/browser/native_window.h | 4 ++-- atom/browser/native_window_observer.h | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/atom/browser/api/atom_api_window.cc b/atom/browser/api/atom_api_window.cc index ef38490d8f7b..fc6c4eaffb74 100644 --- a/atom/browser/api/atom_api_window.cc +++ b/atom/browser/api/atom_api_window.cc @@ -227,12 +227,12 @@ void Window::OnWindowLeaveFullScreen() { Emit("leave-full-screen"); } -void Window::OnWindowScrollTouchUp() { - Emit("scroll-touch-up"); +void Window::OnWindowScrollTouchBegin() { + Emit("scroll-touch-begin"); } -void Window::OnWindowScrollTouchDown() { - Emit("scroll-touch-down"); +void Window::OnWindowScrollTouchEnd() { + Emit("scroll-touch-end"); } void Window::OnWindowEnterHtmlFullScreen() { diff --git a/atom/browser/api/atom_api_window.h b/atom/browser/api/atom_api_window.h index 12eec04680f5..ccbb9ab5f588 100644 --- a/atom/browser/api/atom_api_window.h +++ b/atom/browser/api/atom_api_window.h @@ -65,8 +65,8 @@ class Window : public mate::TrackableObject, void OnWindowResize() override; void OnWindowMove() override; void OnWindowMoved() override; - void OnWindowScrollTouchUp() override; - void OnWindowScrollTouchDown() override; + void OnWindowScrollTouchBegin() override; + void OnWindowScrollTouchEnd() override; void OnWindowEnterFullScreen() override; void OnWindowLeaveFullScreen() override; void OnWindowEnterHtmlFullScreen() override; diff --git a/atom/browser/native_window.cc b/atom/browser/native_window.cc index 89354381bead..21b32797e8a6 100644 --- a/atom/browser/native_window.cc +++ b/atom/browser/native_window.cc @@ -442,14 +442,14 @@ void NativeWindow::NotifyWindowEnterFullScreen() { OnWindowEnterFullScreen()); } -void NativeWindow::NotifyWindowScrollTouchUp() { +void NativeWindow::NotifyWindowScrollTouchBegin() { FOR_EACH_OBSERVER(NativeWindowObserver, observers_, - OnWindowScrollTouchUp()); + OnWindowScrollTouchBegin()); } -void NativeWindow::NotifyWindowScrollTouchDown() { +void NativeWindow::NotifyWindowScrollTouchEnd() { FOR_EACH_OBSERVER(NativeWindowObserver, observers_, - OnWindowScrollTouchDown()); + OnWindowScrollTouchEnd()); } void NativeWindow::NotifyWindowLeaveFullScreen() { diff --git a/atom/browser/native_window.h b/atom/browser/native_window.h index f1dac26650c7..418f0f35791e 100644 --- a/atom/browser/native_window.h +++ b/atom/browser/native_window.h @@ -205,8 +205,8 @@ class NativeWindow : public base::SupportsUserData, void NotifyWindowMove(); void NotifyWindowResize(); void NotifyWindowMoved(); - void NotifyWindowScrollTouchUp(); - void NotifyWindowScrollTouchDown(); + void NotifyWindowScrollTouchBegin(); + void NotifyWindowScrollTouchEnd(); void NotifyWindowEnterFullScreen(); void NotifyWindowLeaveFullScreen(); void NotifyWindowEnterHtmlFullScreen(); diff --git a/atom/browser/native_window_observer.h b/atom/browser/native_window_observer.h index b7ba9d756f11..4af181085a08 100644 --- a/atom/browser/native_window_observer.h +++ b/atom/browser/native_window_observer.h @@ -50,8 +50,8 @@ class NativeWindowObserver { virtual void OnWindowResize() {} virtual void OnWindowMove() {} virtual void OnWindowMoved() {} - virtual void OnWindowScrollTouchUp() {} - virtual void OnWindowScrollTouchDown() {} + virtual void OnWindowScrollTouchBegin() {} + virtual void OnWindowScrollTouchEnd() {} virtual void OnWindowEnterFullScreen() {} virtual void OnWindowLeaveFullScreen() {} virtual void OnWindowEnterHtmlFullScreen() {} From e96e674201c7dfccf7f39f8b22711f3a803de7e9 Mon Sep 17 00:00:00 2001 From: Ben Gotow Date: Thu, 21 Jan 2016 16:31:31 -0800 Subject: [PATCH 3/4] Properly clean up NSEvent monitor --- atom/browser/native_window_mac.h | 1 + atom/browser/native_window_mac.mm | 33 +++++++++++++++++-------------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/atom/browser/native_window_mac.h b/atom/browser/native_window_mac.h index ef3ccb150217..927e971d9bbd 100644 --- a/atom/browser/native_window_mac.h +++ b/atom/browser/native_window_mac.h @@ -110,6 +110,7 @@ class NativeWindowMac : public NativeWindow { base::scoped_nsobject window_; base::scoped_nsobject window_delegate_; + base::scoped_nsobject event_monitor_; // The view that will fill the whole frameless window. base::scoped_nsobject content_view_; diff --git a/atom/browser/native_window_mac.mm b/atom/browser/native_window_mac.mm index 198a78e3f594..88476eafbf54 100644 --- a/atom/browser/native_window_mac.mm +++ b/atom/browser/native_window_mac.mm @@ -491,28 +491,31 @@ NativeWindowMac::NativeWindowMac( [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; BOOL __block down = NO; - [NSEvent addLocalMonitorForEventsMatchingMask:NSScrollWheelMask handler:^NSEvent * _Nullable(NSEvent * event) { - if (![window_ isKeyWindow]) - return event; + event_monitor_.reset([[NSEvent + addLocalMonitorForEventsMatchingMask:NSScrollWheelMask + handler:^NSEvent * _Nullable(NSEvent * event) { + if (![window_ isKeyWindow]) + return event; - if (!web_contents) - return event; + if (!web_contents) + return event; - if (!down && (([event phase] == NSEventPhaseMayBegin) || ([event phase] == NSEventPhaseBegan))) { - this->NotifyWindowScrollTouchDown(); - down = YES; - } - if (down && (([event phase] == NSEventPhaseEnded) || ([event phase] == NSEventPhaseCancelled))) { - this->NotifyWindowScrollTouchUp(); - down = NO; - } - return event; - }]; + if (!down && (([event phase] == NSEventPhaseMayBegin) || ([event phase] == NSEventPhaseBegan))) { + this->NotifyWindowScrollTouchBegin(); + down = YES; + } + if (down && (([event phase] == NSEventPhaseEnded) || ([event phase] == NSEventPhaseCancelled))) { + this->NotifyWindowScrollTouchEnd(); + down = NO; + } + return event; + }] retain]); InstallView(); } NativeWindowMac::~NativeWindowMac() { + [NSEvent removeMonitor: event_monitor_.get()]; Observe(nullptr); } From d186dceb4bf6af7917e8bd5a076859d3ab34c901 Mon Sep 17 00:00:00 2001 From: Ben Gotow Date: Thu, 21 Jan 2016 16:36:48 -0800 Subject: [PATCH 4/4] Add correct window check for wheel events. See description Tested that with two windows, events are fired on the correct BrowserWindow instance and not both, and that scrolling a window which is in the background works properly. --- atom/browser/native_window_mac.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atom/browser/native_window_mac.mm b/atom/browser/native_window_mac.mm index 88476eafbf54..133a0e6becc5 100644 --- a/atom/browser/native_window_mac.mm +++ b/atom/browser/native_window_mac.mm @@ -494,7 +494,7 @@ NativeWindowMac::NativeWindowMac( event_monitor_.reset([[NSEvent addLocalMonitorForEventsMatchingMask:NSScrollWheelMask handler:^NSEvent * _Nullable(NSEvent * event) { - if (![window_ isKeyWindow]) + if ([[event window] windowNumber] != [window_ windowNumber]) return event; if (!web_contents)