UI/AppKit: Do not place the WebView inside a scroll view

Now that scrolling and rendering scrollbars is handled entirely in the
WebContent process, there's no reason to place the WebView inside scroll
views.
This commit is contained in:
Timothy Flynn 2024-11-07 12:26:42 -05:00 committed by Andreas Kling
commit 43ed03145d
Notes: github-actions[bot] 2024-11-07 21:54:17 +00:00
12 changed files with 43 additions and 119 deletions

View file

@ -14,7 +14,7 @@
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*, Web::UIEvents::MouseButton);
Web::DragEvent ns_event_to_drag_event(Web::DragEvent::Type, id<NSDraggingInfo>, NSView*); Web::DragEvent ns_event_to_drag_event(Web::DragEvent::Type, id<NSDraggingInfo>, NSView*);
Vector<URL::URL> drag_event_url_list(Web::DragEvent const&); Vector<URL::URL> drag_event_url_list(Web::DragEvent const&);

View file

@ -40,7 +40,7 @@ static Web::UIEvents::KeyModifier ns_modifiers_to_key_modifiers(NSEventModifierF
return static_cast<Web::UIEvents::KeyModifier>(modifiers); return static_cast<Web::UIEvents::KeyModifier>(modifiers);
} }
Web::MouseEvent ns_event_to_mouse_event(Web::MouseEvent::Type type, NSEvent* event, NSView* view, NSScrollView* scroll_view, Web::UIEvents::MouseButton button) Web::MouseEvent ns_event_to_mouse_event(Web::MouseEvent::Type type, NSEvent* event, NSView* view, Web::UIEvents::MouseButton button)
{ {
auto position = [view convertPoint:event.locationInWindow fromView:nil]; auto position = [view convertPoint:event.locationInWindow fromView:nil];
auto device_position = ns_point_to_gfx_point(position).to_type<Web::DevicePixels>(); auto device_position = ns_point_to_gfx_point(position).to_type<Web::DevicePixels>();
@ -62,8 +62,10 @@ Web::MouseEvent ns_event_to_mouse_event(Web::MouseEvent::Type type, NSEvent* eve
CGFloat delta_y = -[event scrollingDeltaY]; CGFloat delta_y = -[event scrollingDeltaY];
if (![event hasPreciseScrollingDeltas]) { if (![event hasPreciseScrollingDeltas]) {
delta_x *= scroll_view.horizontalLineScroll; static constexpr CGFloat imprecise_scroll_multiplier = 24;
delta_y *= scroll_view.verticalLineScroll;
delta_x *= imprecise_scroll_multiplier;
delta_y *= imprecise_scroll_multiplier;
} }
wheel_delta_x = static_cast<int>(delta_x); wheel_delta_x = static_cast<int>(delta_x);

View file

@ -129,7 +129,7 @@ static constexpr NSInteger CONTEXT_MENU_DELETE_COOKIE_TAG = 4;
[NSMenu popUpContextMenu:strong_self.cookie_context_menu withEvent:event forView:strong_self.web_view]; [NSMenu popUpContextMenu:strong_self.cookie_context_menu withEvent:event forView:strong_self.web_view];
}; };
[self setContentView:self.web_view.enclosingScrollView]; [self setContentView:self.web_view];
[self setTitle:@"Inspector"]; [self setTitle:@"Inspector"];
[self setIsVisible:YES]; [self setIsVisible:YES];
} }

View file

@ -48,7 +48,7 @@
@end @end
@interface LadybirdWebView : NSClipView <NSMenuDelegate> @interface LadybirdWebView : NSView <NSMenuDelegate>
- (instancetype)init:(id<LadybirdWebViewObserver>)observer; - (instancetype)init:(id<LadybirdWebViewObserver>)observer;
- (instancetype)initAsChild:(id<LadybirdWebViewObserver>)observer - (instancetype)initAsChild:(id<LadybirdWebViewObserver>)observer
@ -70,7 +70,6 @@
- (void)handleResize; - (void)handleResize;
- (void)handleDevicePixelRatioChange; - (void)handleDevicePixelRatioChange;
- (void)handleScroll;
- (void)handleVisibility:(BOOL)is_visible; - (void)handleVisibility:(BOOL)is_visible;
- (void)setPreferredColorScheme:(Web::CSS::PreferredColorScheme)color_scheme; - (void)setPreferredColorScheme:(Web::CSS::PreferredColorScheme)color_scheme;

View file

@ -196,20 +196,14 @@ struct HideCursor {
- (void)handleResize - (void)handleResize
{ {
[self updateViewportRect:Ladybird::WebViewBridge::ForResize::Yes]; [self updateViewportRect];
[self updateStatusLabelPosition]; [self updateStatusLabelPosition];
} }
- (void)handleDevicePixelRatioChange - (void)handleDevicePixelRatioChange
{ {
m_web_view_bridge->set_device_pixel_ratio([[self window] backingScaleFactor]); m_web_view_bridge->set_device_pixel_ratio([[self window] backingScaleFactor]);
[self updateViewportRect:Ladybird::WebViewBridge::ForResize::Yes]; [self updateViewportRect];
[self updateStatusLabelPosition];
}
- (void)handleScroll
{
[self updateViewportRect:Ladybird::WebViewBridge::ForResize::No];
[self updateStatusLabelPosition]; [self updateStatusLabelPosition];
} }
@ -295,27 +289,10 @@ static void copy_data_to_clipboard(StringView data, NSPasteboardType pasteboard_
[pasteBoard setData:ns_data forType:pasteboard_type]; [pasteBoard setData:ns_data forType:pasteboard_type];
} }
- (void)updateViewportRect:(Ladybird::WebViewBridge::ForResize)for_resize - (void)updateViewportRect
{ {
auto content_rect = [self frame]; auto viewport_rect = Ladybird::ns_rect_to_gfx_rect([self frame]);
auto document_rect = [[self documentView] frame]; m_web_view_bridge->set_viewport_rect(viewport_rect);
auto device_pixel_ratio = m_web_view_bridge->device_pixel_ratio();
auto position = [&](auto content_size, auto document_size, auto scroll) {
return max(0, (document_size - content_size) * device_pixel_ratio * scroll);
};
auto horizontal_scroll = [[[self scrollView] horizontalScroller] floatValue];
auto vertical_scroll = [[[self scrollView] verticalScroller] floatValue];
auto ns_viewport_rect = NSMakeRect(
position(content_rect.size.width, document_rect.size.width, horizontal_scroll),
position(content_rect.size.height, document_rect.size.height, vertical_scroll),
content_rect.size.width,
content_rect.size.height);
auto viewport_rect = Ladybird::ns_rect_to_gfx_rect(ns_viewport_rect);
m_web_view_bridge->set_viewport_rect(viewport_rect, for_resize);
} }
- (void)updateStatusLabelPosition - (void)updateStatusLabelPosition
@ -339,15 +316,6 @@ static void copy_data_to_clipboard(StringView data, NSPasteboardType pasteboard_
// By default, capturing self will copy a strong reference to self in ARC. // By default, capturing self will copy a strong reference to self in ARC.
__weak LadybirdWebView* weak_self = self; __weak LadybirdWebView* weak_self = self;
m_web_view_bridge->on_did_layout = [weak_self](auto content_size) {
LadybirdWebView* self = weak_self;
if (self == nil) {
return;
}
auto inverse_device_pixel_ratio = m_web_view_bridge->inverse_device_pixel_ratio();
[[self documentView] setFrameSize:NSMakeSize(content_size.width() * inverse_device_pixel_ratio, content_size.height() * inverse_device_pixel_ratio)];
};
m_web_view_bridge->on_ready_to_paint = [weak_self]() { m_web_view_bridge->on_ready_to_paint = [weak_self]() {
LadybirdWebView* self = weak_self; LadybirdWebView* self = weak_self;
if (self == nil) { if (self == nil) {
@ -573,7 +541,7 @@ static void copy_data_to_clipboard(StringView data, NSPasteboardType pasteboard_
if (self == nil) { if (self == nil) {
return; return;
} }
[self updateViewportRect:Ladybird::WebViewBridge::ForResize::Yes]; [self updateViewportRect];
}; };
m_web_view_bridge->on_request_tooltip_override = [weak_self](auto, auto const& tooltip) { m_web_view_bridge->on_request_tooltip_override = [weak_self](auto, auto const& tooltip) {
@ -1080,7 +1048,7 @@ static void copy_data_to_clipboard(StringView data, NSPasteboardType pasteboard_
if (self == nil) { if (self == nil) {
return; return;
} }
self.backgroundColor = Ladybird::gfx_color_to_ns_color(color); self.layer.backgroundColor = [Ladybird::gfx_color_to_ns_color(color) CGColor];
}; };
m_web_view_bridge->on_find_in_page = [weak_self](auto current_match_index, auto const& total_match_count) { m_web_view_bridge->on_find_in_page = [weak_self](auto current_match_index, auto const& total_match_count) {
@ -1138,11 +1106,6 @@ static void copy_data_to_clipboard(StringView data, NSPasteboardType pasteboard_
m_web_view_bridge->color_picker_update(Ladybird::ns_color_to_gfx_color([NSColorPanel sharedColorPanel].color), Web::HTML::ColorPickerUpdateState::Closed); m_web_view_bridge->color_picker_update(Ladybird::ns_color_to_gfx_color([NSColorPanel sharedColorPanel].color), Web::HTML::ColorPickerUpdateState::Closed);
} }
- (NSScrollView*)scrollView
{
return (NSScrollView*)[self superview];
}
- (void)copy:(id)sender - (void)copy:(id)sender
{ {
copy_data_to_clipboard(m_web_view_bridge->selected_text(), NSPasteboardTypeString); copy_data_to_clipboard(m_web_view_bridge->selected_text(), NSPasteboardTypeString);
@ -1584,13 +1547,13 @@ static void copy_data_to_clipboard(StringView data, NSPasteboardType pasteboard_
- (void)mouseMoved:(NSEvent*)event - (void)mouseMoved:(NSEvent*)event
{ {
auto mouse_event = Ladybird::ns_event_to_mouse_event(Web::MouseEvent::Type::MouseMove, event, self, [self scrollView], Web::UIEvents::MouseButton::None); auto mouse_event = Ladybird::ns_event_to_mouse_event(Web::MouseEvent::Type::MouseMove, event, self, Web::UIEvents::MouseButton::None);
m_web_view_bridge->enqueue_input_event(move(mouse_event)); m_web_view_bridge->enqueue_input_event(move(mouse_event));
} }
- (void)scrollWheel:(NSEvent*)event - (void)scrollWheel:(NSEvent*)event
{ {
auto mouse_event = Ladybird::ns_event_to_mouse_event(Web::MouseEvent::Type::MouseWheel, event, self, [self scrollView], Web::UIEvents::MouseButton::Middle); auto mouse_event = Ladybird::ns_event_to_mouse_event(Web::MouseEvent::Type::MouseWheel, event, self, Web::UIEvents::MouseButton::Middle);
m_web_view_bridge->enqueue_input_event(move(mouse_event)); m_web_view_bridge->enqueue_input_event(move(mouse_event));
} }
@ -1598,19 +1561,19 @@ static void copy_data_to_clipboard(StringView data, NSPasteboardType pasteboard_
{ {
[[self window] makeFirstResponder:self]; [[self window] makeFirstResponder:self];
auto mouse_event = Ladybird::ns_event_to_mouse_event(Web::MouseEvent::Type::MouseDown, event, self, [self scrollView], Web::UIEvents::MouseButton::Primary); auto mouse_event = Ladybird::ns_event_to_mouse_event(Web::MouseEvent::Type::MouseDown, event, self, Web::UIEvents::MouseButton::Primary);
m_web_view_bridge->enqueue_input_event(move(mouse_event)); m_web_view_bridge->enqueue_input_event(move(mouse_event));
} }
- (void)mouseUp:(NSEvent*)event - (void)mouseUp:(NSEvent*)event
{ {
auto mouse_event = Ladybird::ns_event_to_mouse_event(Web::MouseEvent::Type::MouseUp, event, self, [self scrollView], Web::UIEvents::MouseButton::Primary); auto mouse_event = Ladybird::ns_event_to_mouse_event(Web::MouseEvent::Type::MouseUp, event, self, Web::UIEvents::MouseButton::Primary);
m_web_view_bridge->enqueue_input_event(move(mouse_event)); m_web_view_bridge->enqueue_input_event(move(mouse_event));
} }
- (void)mouseDragged:(NSEvent*)event - (void)mouseDragged:(NSEvent*)event
{ {
auto mouse_event = Ladybird::ns_event_to_mouse_event(Web::MouseEvent::Type::MouseMove, event, self, [self scrollView], Web::UIEvents::MouseButton::Primary); auto mouse_event = Ladybird::ns_event_to_mouse_event(Web::MouseEvent::Type::MouseMove, event, self, Web::UIEvents::MouseButton::Primary);
m_web_view_bridge->enqueue_input_event(move(mouse_event)); m_web_view_bridge->enqueue_input_event(move(mouse_event));
} }
@ -1618,19 +1581,19 @@ static void copy_data_to_clipboard(StringView data, NSPasteboardType pasteboard_
{ {
[[self window] makeFirstResponder:self]; [[self window] makeFirstResponder:self];
auto mouse_event = Ladybird::ns_event_to_mouse_event(Web::MouseEvent::Type::MouseDown, event, self, [self scrollView], Web::UIEvents::MouseButton::Secondary); auto mouse_event = Ladybird::ns_event_to_mouse_event(Web::MouseEvent::Type::MouseDown, event, self, Web::UIEvents::MouseButton::Secondary);
m_web_view_bridge->enqueue_input_event(move(mouse_event)); m_web_view_bridge->enqueue_input_event(move(mouse_event));
} }
- (void)rightMouseUp:(NSEvent*)event - (void)rightMouseUp:(NSEvent*)event
{ {
auto mouse_event = Ladybird::ns_event_to_mouse_event(Web::MouseEvent::Type::MouseUp, event, self, [self scrollView], Web::UIEvents::MouseButton::Secondary); auto mouse_event = Ladybird::ns_event_to_mouse_event(Web::MouseEvent::Type::MouseUp, event, self, Web::UIEvents::MouseButton::Secondary);
m_web_view_bridge->enqueue_input_event(move(mouse_event)); m_web_view_bridge->enqueue_input_event(move(mouse_event));
} }
- (void)rightMouseDragged:(NSEvent*)event - (void)rightMouseDragged:(NSEvent*)event
{ {
auto mouse_event = Ladybird::ns_event_to_mouse_event(Web::MouseEvent::Type::MouseMove, event, self, [self scrollView], Web::UIEvents::MouseButton::Secondary); auto mouse_event = Ladybird::ns_event_to_mouse_event(Web::MouseEvent::Type::MouseMove, event, self, Web::UIEvents::MouseButton::Secondary);
m_web_view_bridge->enqueue_input_event(move(mouse_event)); m_web_view_bridge->enqueue_input_event(move(mouse_event));
} }
@ -1641,7 +1604,7 @@ static void copy_data_to_clipboard(StringView data, NSPasteboardType pasteboard_
[[self window] makeFirstResponder:self]; [[self window] makeFirstResponder:self];
auto mouse_event = Ladybird::ns_event_to_mouse_event(Web::MouseEvent::Type::MouseDown, event, self, [self scrollView], Web::UIEvents::MouseButton::Middle); auto mouse_event = Ladybird::ns_event_to_mouse_event(Web::MouseEvent::Type::MouseDown, event, self, Web::UIEvents::MouseButton::Middle);
m_web_view_bridge->enqueue_input_event(move(mouse_event)); m_web_view_bridge->enqueue_input_event(move(mouse_event));
} }
@ -1650,7 +1613,7 @@ static void copy_data_to_clipboard(StringView data, NSPasteboardType pasteboard_
if (event.buttonNumber != 2) if (event.buttonNumber != 2)
return; return;
auto mouse_event = Ladybird::ns_event_to_mouse_event(Web::MouseEvent::Type::MouseUp, event, self, [self scrollView], Web::UIEvents::MouseButton::Middle); auto mouse_event = Ladybird::ns_event_to_mouse_event(Web::MouseEvent::Type::MouseUp, event, self, Web::UIEvents::MouseButton::Middle);
m_web_view_bridge->enqueue_input_event(move(mouse_event)); m_web_view_bridge->enqueue_input_event(move(mouse_event));
} }
@ -1659,7 +1622,7 @@ static void copy_data_to_clipboard(StringView data, NSPasteboardType pasteboard_
if (event.buttonNumber != 2) if (event.buttonNumber != 2)
return; return;
auto mouse_event = Ladybird::ns_event_to_mouse_event(Web::MouseEvent::Type::MouseMove, event, self, [self scrollView], Web::UIEvents::MouseButton::Middle); auto mouse_event = Ladybird::ns_event_to_mouse_event(Web::MouseEvent::Type::MouseMove, event, self, Web::UIEvents::MouseButton::Middle);
m_web_view_bridge->enqueue_input_event(move(mouse_event)); m_web_view_bridge->enqueue_input_event(move(mouse_event));
} }

View file

@ -51,16 +51,12 @@ void WebViewBridge::set_system_visibility_state(bool is_visible)
client().async_set_system_visibility_state(m_client_state.page_index, is_visible); client().async_set_system_visibility_state(m_client_state.page_index, is_visible);
} }
void WebViewBridge::set_viewport_rect(Gfx::IntRect viewport_rect, ForResize for_resize) void WebViewBridge::set_viewport_rect(Gfx::IntRect viewport_rect)
{ {
viewport_rect.set_size(scale_for_device(viewport_rect.size(), m_device_pixel_ratio)); viewport_rect.set_size(scale_for_device(viewport_rect.size(), m_device_pixel_ratio));
m_viewport_size = viewport_rect.size(); m_viewport_size = viewport_rect.size();
client().async_set_viewport_size(m_client_state.page_index, m_viewport_size.to_type<Web::DevicePixels>()); handle_resize();
if (for_resize == ForResize::Yes) {
handle_resize();
}
} }
void WebViewBridge::update_palette() void WebViewBridge::update_palette()

View file

@ -32,12 +32,7 @@ public:
float inverse_device_pixel_ratio() const { return 1.0f / m_device_pixel_ratio; } float inverse_device_pixel_ratio() const { return 1.0f / m_device_pixel_ratio; }
void set_system_visibility_state(bool is_visible); void set_system_visibility_state(bool is_visible);
void set_viewport_rect(Gfx::IntRect);
enum class ForResize {
Yes,
No,
};
void set_viewport_rect(Gfx::IntRect, ForResize = ForResize::No);
void update_palette(); void update_palette();
void set_preferred_color_scheme(Web::CSS::PreferredColorScheme); void set_preferred_color_scheme(Web::CSS::PreferredColorScheme);

View file

@ -28,36 +28,16 @@
if (self) { if (self) {
self.web_view = web_view; self.web_view = web_view;
if (self.web_view == nil) if (self.web_view == nil)
self.web_view = [[LadybirdWebView alloc] init:nil]; self.web_view = [[LadybirdWebView alloc] init:nil];
[self.web_view setPostsBoundsChangedNotifications:YES]; [self.web_view setClipsToBounds:YES];
auto* scroll_view = [[NSScrollView alloc] init];
[scroll_view setHasVerticalScroller:NO];
[scroll_view setHasHorizontalScroller:NO];
[scroll_view setLineScroll:24];
[scroll_view setContentView:self.web_view];
[scroll_view setDocumentView:[[NSView alloc] init]];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(onContentScroll:)
name:NSViewBoundsDidChangeNotification
object:[scroll_view contentView]];
} }
return self; return self;
} }
#pragma mark - Private methods
- (void)onContentScroll:(NSNotification*)notification
{
[self.web_view handleScroll];
}
#pragma mark - NSWindow #pragma mark - NSWindow
- (void)setIsVisible:(BOOL)flag - (void)setIsVisible:(BOOL)flag

View file

@ -95,7 +95,7 @@ static constexpr CGFloat const WINDOW_HEIGHT = 800;
auto* stack_view = [NSStackView stackViewWithViews:@[ auto* stack_view = [NSStackView stackViewWithViews:@[
self.search_panel, self.search_panel,
self.web_view.enclosingScrollView, self.web_view,
]]; ]];
[stack_view setOrientation:NSUserInterfaceLayoutOrientationVertical]; [stack_view setOrientation:NSUserInterfaceLayoutOrientationVertical];

View file

@ -46,7 +46,7 @@ static constexpr CGFloat const WINDOW_HEIGHT = 400;
[strong_self updateStatistics]; [strong_self updateStatistics];
}); });
[self setContentView:self.web_view.enclosingScrollView]; [self setContentView:self.web_view];
[self setTitle:@"Task Manager"]; [self setTitle:@"Task Manager"];
[self setIsVisible:YES]; [self setIsVisible:YES];

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org> * Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
* Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org> * Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
@ -8,30 +8,23 @@
import Foundation import Foundation
import Ladybird.WebView import Ladybird.WebView
import Ladybird.WebViewApplication import Ladybird.WebViewApplication
import Ladybird.WebViewWindow
import SwiftUI import SwiftUI
public class TaskManager: NSWindow { public class TaskManager: LadybirdWebViewWindow {
private let WINDOW_WIDTH: CGFloat = 600 private let WINDOW_WIDTH: CGFloat = 600
private let WINDOW_HEIGHT: CGFloat = 400 private let WINDOW_HEIGHT: CGFloat = 400
var web_view: LadybirdWebView
private var timer: Timer? private var timer: Timer?
init() { init() {
let tab_rect = NSApplication.shared.keyWindow!.frame let tab_rect = NSApplication.shared.keyWindow!.frame
let position_x = tab_rect.origin.x + (tab_rect.size.width - WINDOW_WIDTH) / 2 let position_x = tab_rect.origin.x + (tab_rect.size.width - WINDOW_WIDTH) / 2
let position_y = tab_rect.origin.y + (tab_rect.size.height - WINDOW_HEIGHT) / 2 let position_y = tab_rect.origin.y + (tab_rect.size.height - WINDOW_HEIGHT) / 2
let window_rect = NSMakeRect(position_x, position_y, WINDOW_WIDTH, WINDOW_HEIGHT) let window_rect = NSMakeRect(position_x, position_y, WINDOW_WIDTH, WINDOW_HEIGHT)
let style_mask = NSWindow.StyleMask.init(arrayLiteral: [
NSWindow.StyleMask.titled, NSWindow.StyleMask.closable, NSWindow.StyleMask.miniaturizable,
NSWindow.StyleMask.resizable,
])
self.web_view = LadybirdWebView.init(nil) super.init(webView: nil, windowRect: window_rect)
super.init(contentRect: window_rect, styleMask: style_mask, backing: NSWindow.BackingStoreType.buffered, defer: false)
self.timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] timer in self.timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] timer in
if let strong_self = self { if let strong_self = self {
@ -39,16 +32,7 @@ public class TaskManager: NSWindow {
} }
} }
self.web_view.postsBoundsChangedNotifications = true self.contentView = self.web_view
let scroll_view = NSScrollView()
scroll_view.hasVerticalScroller = true
scroll_view.hasHorizontalScroller = true
scroll_view.lineScroll = 24
scroll_view.contentView = self.web_view
scroll_view.documentView = NSView()
self.contentView = scroll_view
self.title = "Task Manager" self.title = "Task Manager"
self.setIsVisible(true) self.setIsVisible(true)

View file

@ -7,6 +7,11 @@ module Ladybird [system] {
export * export *
} }
explicit module WebViewWindow {
header "UI/LadybirdWebViewWindow.h"
export *
}
explicit module WebViewApplication { explicit module WebViewApplication {
header "../../Userland/Libraries/LibWebView/Application.h" header "../../Userland/Libraries/LibWebView/Application.h"
export * export *