blob: 2ecff6bd8fd9f1f7347a9f67c2f461adbfcdfb66 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/image_editor/event_capture_mac.h"
#import <Cocoa/Cocoa.h>
#include <memory>
#include "base/apple/owned_objc.h"
#include "base/check.h"
#include "base/functional/callback.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#import "components/remote_cocoa/app_shim/mouse_capture.h"
#import "components/remote_cocoa/app_shim/mouse_capture_delegate.h"
#include "ui/events/event.h"
#include "ui/events/event_utils.h"
namespace image_editor {
class EventCaptureMac::MouseCaptureDelegateImpl
: public remote_cocoa::CocoaMouseCaptureDelegate {
public:
MouseCaptureDelegateImpl(ui::EventHandler* event_handler,
base::OnceClosure capture_lost_callback,
gfx::NativeView web_contents_view,
gfx::NativeWindow target_native_window)
: event_handler_(event_handler),
capture_lost_callback_(std::move(capture_lost_callback)),
web_contents_view_(web_contents_view.GetNativeNSView()),
window_(target_native_window.GetNativeNSWindow()),
mouse_capture_(
std::make_unique<remote_cocoa::CocoaMouseCapture>(this)) {}
void SetKeyboardMonitor(id local_keyboard_monitor) {
local_keyboard_monitor_ = local_keyboard_monitor;
}
void Reset() {
// We do not want our callback to run if mouse capture loss was caused by
// reset of event capture.
std::move(capture_lost_callback_).Reset();
// Remove the key down monitor.
[NSEvent removeMonitor:local_keyboard_monitor_];
}
private:
// remote_cocoa::CocoaMouseCaptureDelegate:
bool PostCapturedEvent(NSEvent* event) override {
std::unique_ptr<ui::Event> ui_event =
ui::EventFromNative(base::apple::OwnedNSEvent(event));
if (!ui_event) {
return false;
}
// The window from where the event is sourced. If it is outside of the
// browser, this window will not be equal to GetWindow().
NSView* view = [event.window.contentView hitTest:event.locationInWindow];
ui::EventType type = ui_event->type();
if (type == ui::EventType::kMouseDragged ||
type == ui::EventType::kMouseReleased) {
event_handler_->OnMouseEvent(ui_event->AsMouseEvent());
} else if ((type == ui::EventType::kMousePressed ||
type == ui::EventType::kMouseMoved) &&
web_contents_view_ == view) {
// We do not need to record mouse clicks outside of the web contents.
event_handler_->OnMouseEvent(ui_event->AsMouseEvent());
} else if (type == ui::EventType::kMouseMoved &&
web_contents_view_ != view) {
// Manually set arrow cursor when region search UI is open and cursor is
// moved from web contents.
[NSCursor.arrowCursor set];
} else if (type == ui::EventType::kScroll) {
event_handler_->OnScrollEvent(ui_event->AsScrollEvent());
}
// If we set the ui event as handled, then we want to swallow the event.
return ui_event->handled();
}
void OnMouseCaptureLost() override {
if (!capture_lost_callback_.is_null()) {
std::move(capture_lost_callback_).Run();
}
}
NSWindow* GetWindow() const override { return window_; }
raw_ptr<ui::EventHandler> event_handler_;
base::OnceClosure capture_lost_callback_;
NSView* __weak web_contents_view_ = nil;
NSWindow* __weak window_ = nil;
std::unique_ptr<remote_cocoa::CocoaMouseCapture> mouse_capture_;
id __strong local_keyboard_monitor_ = nil;
};
EventCaptureMac::EventCaptureMac(ui::EventHandler* event_handler,
base::OnceClosure capture_lost_callback,
gfx::NativeView web_contents_view,
gfx::NativeWindow target_native_window)
: mouse_capture_delegate_impl_(std::make_unique<MouseCaptureDelegateImpl>(
event_handler,
std::move(capture_lost_callback),
web_contents_view,
target_native_window)) {
CreateKeyDownLocalMonitor(event_handler, target_native_window);
}
void EventCaptureMac::CreateKeyDownLocalMonitor(
ui::EventHandler* event_handler,
gfx::NativeWindow target_native_window) {
DCHECK(event_handler);
NSWindow* target_window = target_native_window.GetNativeNSWindow();
// Capture a WeakPtr. This allows the block to detect another event monitor
// for the same event deleting |this|.
base::WeakPtr<EventCaptureMac> weak_ptr = factory_.GetWeakPtr();
auto block = ^NSEvent*(NSEvent* event) {
if (!weak_ptr) {
return event;
}
if (!target_window || event.window == target_window) {
std::unique_ptr<ui::Event> ui_event =
ui::EventFromNative(base::apple::OwnedNSEvent(event));
if (!ui_event) {
return event;
}
ui::EventType type = ui_event->type();
if (type == ui::EventType::kKeyPressed) {
event_handler->OnKeyEvent(ui_event->AsKeyEvent());
}
// Consume the event if allowed and the corresponding EventHandler method
// requested.
if (ui_event->cancelable() && ui_event->handled()) {
return nil;
}
}
return event;
};
mouse_capture_delegate_impl_->SetKeyboardMonitor([NSEvent
addLocalMonitorForEventsMatchingMask:NSEventMaskKeyDown
handler:block]);
}
EventCaptureMac::~EventCaptureMac() {
mouse_capture_delegate_impl_->Reset();
}
void EventCaptureMac::SetCrossCursor() {
[NSCursor.crosshairCursor set];
}
} // namespace image_editor