mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-29 20:29:18 +00:00
UI/AppKit: Process drag-and-drop events through the web view
This forwards all drag-and-drop events from the UI to the WebContent process. If the page accepts the events, the UI does not handle them. Otherwise, we will open the dropped files as file:// URLs.
This commit is contained in:
parent
948b6de3b1
commit
ac062d0c97
Notes:
github-actions[bot]
2024-08-19 11:30:14 +00:00
Author: https://github.com/trflynn89
Commit: ac062d0c97
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/1111
5 changed files with 138 additions and 3 deletions
|
@ -1,11 +1,13 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
* Copyright (c) 2023-2024, Tim Flynn <trflynn89@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/Vector.h>
|
||||||
|
#include <LibURL/Forward.h>
|
||||||
#include <LibWeb/Page/InputEvent.h>
|
#include <LibWeb/Page/InputEvent.h>
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
|
@ -13,6 +15,10 @@
|
||||||
namespace Ladybird {
|
namespace Ladybird {
|
||||||
|
|
||||||
Web::MouseEvent ns_event_to_mouse_event(Web::MouseEvent::Type, NSEvent*, NSView*, NSScrollView*, Web::UIEvents::MouseButton);
|
Web::MouseEvent ns_event_to_mouse_event(Web::MouseEvent::Type, NSEvent*, NSView*, NSScrollView*, Web::UIEvents::MouseButton);
|
||||||
|
|
||||||
|
Web::DragEvent ns_event_to_drag_event(Web::DragEvent::Type, id<NSDraggingInfo>, NSView*);
|
||||||
|
Vector<URL::URL> drag_event_url_list(Web::DragEvent const&);
|
||||||
|
|
||||||
Web::KeyEvent ns_event_to_key_event(Web::KeyEvent::Type, NSEvent*);
|
Web::KeyEvent ns_event_to_key_event(Web::KeyEvent::Type, NSEvent*);
|
||||||
NSEvent* key_event_to_ns_event(Web::KeyEvent const&);
|
NSEvent* key_event_to_ns_event(Web::KeyEvent const&);
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
|
* Copyright (c) 2023-2024, Tim Flynn <trflynn89@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/TypeCasts.h>
|
#include <AK/TypeCasts.h>
|
||||||
#include <AK/Utf8View.h>
|
#include <AK/Utf8View.h>
|
||||||
|
#include <LibURL/URL.h>
|
||||||
|
#include <LibWeb/HTML/SelectedFile.h>
|
||||||
|
#include <LibWeb/UIEvents/KeyCode.h>
|
||||||
|
|
||||||
#import <Carbon/Carbon.h>
|
#import <Carbon/Carbon.h>
|
||||||
#import <UI/Event.h>
|
#import <UI/Event.h>
|
||||||
|
@ -70,6 +73,66 @@ Web::MouseEvent ns_event_to_mouse_event(Web::MouseEvent::Type type, NSEvent* eve
|
||||||
return { type, device_position, device_screen_position, button, button, modifiers, wheel_delta_x, wheel_delta_y, nullptr };
|
return { type, device_position, device_screen_position, button, button, modifiers, wheel_delta_x, wheel_delta_y, nullptr };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct DragData : public Web::ChromeInputData {
|
||||||
|
explicit DragData(Vector<URL::URL> urls)
|
||||||
|
: urls(move(urls))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<URL::URL> urls;
|
||||||
|
};
|
||||||
|
|
||||||
|
Web::DragEvent ns_event_to_drag_event(Web::DragEvent::Type type, id<NSDraggingInfo> event, NSView* view)
|
||||||
|
{
|
||||||
|
auto position = [view convertPoint:event.draggingLocation fromView:nil];
|
||||||
|
auto device_position = ns_point_to_gfx_point(position).to_type<Web::DevicePixels>();
|
||||||
|
|
||||||
|
auto screen_position = [NSEvent mouseLocation];
|
||||||
|
auto device_screen_position = ns_point_to_gfx_point(screen_position).to_type<Web::DevicePixels>();
|
||||||
|
|
||||||
|
auto button = Web::UIEvents::MouseButton::Primary;
|
||||||
|
auto modifiers = ns_modifiers_to_key_modifiers([NSEvent modifierFlags], button);
|
||||||
|
|
||||||
|
Vector<Web::HTML::SelectedFile> files;
|
||||||
|
OwnPtr<DragData> chrome_data;
|
||||||
|
|
||||||
|
auto for_each_file = [&](auto callback) {
|
||||||
|
NSArray* file_list = [[event draggingPasteboard] readObjectsForClasses:@[ [NSURL class] ]
|
||||||
|
options:nil];
|
||||||
|
|
||||||
|
for (NSURL* file in file_list) {
|
||||||
|
auto file_path = Ladybird::ns_string_to_byte_string([file path]);
|
||||||
|
callback(file_path);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (type == Web::DragEvent::Type::DragStart) {
|
||||||
|
for_each_file([&](ByteString const& file_path) {
|
||||||
|
if (auto file = Web::HTML::SelectedFile::from_file_path(file_path); file.is_error())
|
||||||
|
warnln("Unable to open file {}: {}", file_path, file.error());
|
||||||
|
else
|
||||||
|
files.append(file.release_value());
|
||||||
|
});
|
||||||
|
} else if (type == Web::DragEvent::Type::Drop) {
|
||||||
|
Vector<URL::URL> urls;
|
||||||
|
|
||||||
|
for_each_file([&](ByteString const& file_path) {
|
||||||
|
if (auto url = URL::create_with_url_or_path(file_path); url.is_valid())
|
||||||
|
urls.append(move(url));
|
||||||
|
});
|
||||||
|
|
||||||
|
chrome_data = make<DragData>(move(urls));
|
||||||
|
}
|
||||||
|
|
||||||
|
return { type, device_position, device_screen_position, button, button, modifiers, move(files), move(chrome_data) };
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<URL::URL> drag_event_url_list(Web::DragEvent const& event)
|
||||||
|
{
|
||||||
|
auto& chrome_data = verify_cast<DragData>(*event.chrome_data);
|
||||||
|
return move(chrome_data.urls);
|
||||||
|
}
|
||||||
|
|
||||||
NSEvent* create_context_menu_mouse_event(NSView* view, Gfx::IntPoint position)
|
NSEvent* create_context_menu_mouse_event(NSView* view, Gfx::IntPoint position)
|
||||||
{
|
{
|
||||||
return create_context_menu_mouse_event(view, gfx_point_to_ns_point(position));
|
return create_context_menu_mouse_event(view, gfx_point_to_ns_point(position));
|
||||||
|
|
|
@ -48,7 +48,7 @@ struct HideCursor {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@interface LadybirdWebView ()
|
@interface LadybirdWebView () <NSDraggingDestination>
|
||||||
{
|
{
|
||||||
OwnPtr<Ladybird::WebViewBridge> m_web_view_bridge;
|
OwnPtr<Ladybird::WebViewBridge> m_web_view_bridge;
|
||||||
|
|
||||||
|
@ -114,6 +114,8 @@ struct HideCursor {
|
||||||
owner:self
|
owner:self
|
||||||
userInfo:nil];
|
userInfo:nil];
|
||||||
[self addTrackingArea:area];
|
[self addTrackingArea:area];
|
||||||
|
|
||||||
|
[self registerForDraggedTypes:[NSArray arrayWithObjects:NSPasteboardTypeFileURL, nil]];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
@ -417,6 +419,25 @@ static void copy_data_to_clipboard(StringView data, NSPasteboardType pasteboard_
|
||||||
self.event_being_redispatched = nil;
|
self.event_being_redispatched = nil;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
m_web_view_bridge->on_finish_handling_drag_event = [weak_self](auto const& event) {
|
||||||
|
LadybirdWebView* self = weak_self;
|
||||||
|
if (self == nil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type != Web::DragEvent::Type::Drop) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto urls = Ladybird::drag_event_url_list(event); !urls.is_empty()) {
|
||||||
|
[self.observer loadURL:urls[0]];
|
||||||
|
|
||||||
|
for (size_t i = 1; i < urls.size(); ++i) {
|
||||||
|
[self.observer onCreateNewTab:urls[i] activateTab:Web::HTML::ActivateTab::No];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
m_web_view_bridge->on_cursor_change = [weak_self](auto cursor) {
|
m_web_view_bridge->on_cursor_change = [weak_self](auto cursor) {
|
||||||
LadybirdWebView* self = weak_self;
|
LadybirdWebView* self = weak_self;
|
||||||
if (self == nil) {
|
if (self == nil) {
|
||||||
|
@ -1669,4 +1690,41 @@ static void copy_data_to_clipboard(StringView data, NSPasteboardType pasteboard_
|
||||||
m_web_view_bridge->enqueue_input_event(move(key_event));
|
m_web_view_bridge->enqueue_input_event(move(key_event));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - NSDraggingDestination
|
||||||
|
|
||||||
|
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)event
|
||||||
|
{
|
||||||
|
auto drag_event = Ladybird::ns_event_to_drag_event(Web::DragEvent::Type::DragStart, event, self);
|
||||||
|
m_web_view_bridge->enqueue_input_event(move(drag_event));
|
||||||
|
|
||||||
|
return NSDragOperationCopy;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)event
|
||||||
|
{
|
||||||
|
auto drag_event = Ladybird::ns_event_to_drag_event(Web::DragEvent::Type::DragMove, event, self);
|
||||||
|
m_web_view_bridge->enqueue_input_event(move(drag_event));
|
||||||
|
|
||||||
|
return NSDragOperationCopy;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)draggingExited:(id<NSDraggingInfo>)event
|
||||||
|
{
|
||||||
|
auto drag_event = Ladybird::ns_event_to_drag_event(Web::DragEvent::Type::DragEnd, event, self);
|
||||||
|
m_web_view_bridge->enqueue_input_event(move(drag_event));
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)performDragOperation:(id<NSDraggingInfo>)event
|
||||||
|
{
|
||||||
|
auto drag_event = Ladybird::ns_event_to_drag_event(Web::DragEvent::Type::Drop, event, self);
|
||||||
|
m_web_view_bridge->enqueue_input_event(move(drag_event));
|
||||||
|
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)wantsPeriodicDraggingUpdates
|
||||||
|
{
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -93,6 +93,13 @@ void WebViewBridge::enqueue_input_event(Web::MouseEvent event)
|
||||||
ViewImplementation::enqueue_input_event(move(event));
|
ViewImplementation::enqueue_input_event(move(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WebViewBridge::enqueue_input_event(Web::DragEvent event)
|
||||||
|
{
|
||||||
|
event.position = to_content_position(event.position.to_type<int>()).to_type<Web::DevicePixels>();
|
||||||
|
event.screen_position = to_content_position(event.screen_position.to_type<int>()).to_type<Web::DevicePixels>();
|
||||||
|
ViewImplementation::enqueue_input_event(move(event));
|
||||||
|
}
|
||||||
|
|
||||||
void WebViewBridge::enqueue_input_event(Web::KeyEvent event)
|
void WebViewBridge::enqueue_input_event(Web::KeyEvent event)
|
||||||
{
|
{
|
||||||
ViewImplementation::enqueue_input_event(move(event));
|
ViewImplementation::enqueue_input_event(move(event));
|
||||||
|
|
|
@ -44,6 +44,7 @@ public:
|
||||||
void set_preferred_motion(Web::CSS::PreferredMotion);
|
void set_preferred_motion(Web::CSS::PreferredMotion);
|
||||||
|
|
||||||
void enqueue_input_event(Web::MouseEvent);
|
void enqueue_input_event(Web::MouseEvent);
|
||||||
|
void enqueue_input_event(Web::DragEvent);
|
||||||
void enqueue_input_event(Web::KeyEvent);
|
void enqueue_input_event(Web::KeyEvent);
|
||||||
|
|
||||||
struct Paintable {
|
struct Paintable {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue