mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 07:30:19 +00:00
Ladybird/AppKit: Implement a basic find-in-page panel
This commit is contained in:
parent
8d4cd15cb1
commit
d6732e5906
Notes:
sideshowbarker
2024-07-16 23:52:22 +09:00
Author: https://github.com/trflynn89 Commit: https://github.com/SerenityOS/serenity/commit/d6732e5906 Pull-request: https://github.com/SerenityOS/serenity/pull/24489 Reviewed-by: https://github.com/nico ✅
8 changed files with 276 additions and 5 deletions
|
@ -354,6 +354,20 @@
|
||||||
[submenu addItem:[[NSMenuItem alloc] initWithTitle:@"Select All"
|
[submenu addItem:[[NSMenuItem alloc] initWithTitle:@"Select All"
|
||||||
action:@selector(selectAll:)
|
action:@selector(selectAll:)
|
||||||
keyEquivalent:@"a"]];
|
keyEquivalent:@"a"]];
|
||||||
|
[submenu addItem:[NSMenuItem separatorItem]];
|
||||||
|
|
||||||
|
[submenu addItem:[[NSMenuItem alloc] initWithTitle:@"Find..."
|
||||||
|
action:@selector(find:)
|
||||||
|
keyEquivalent:@"f"]];
|
||||||
|
[submenu addItem:[[NSMenuItem alloc] initWithTitle:@"Find Next"
|
||||||
|
action:@selector(findNextMatch:)
|
||||||
|
keyEquivalent:@"g"]];
|
||||||
|
[submenu addItem:[[NSMenuItem alloc] initWithTitle:@"Find Previous"
|
||||||
|
action:@selector(findPreviousMatch:)
|
||||||
|
keyEquivalent:@"G"]];
|
||||||
|
[submenu addItem:[[NSMenuItem alloc] initWithTitle:@"Use Selection for Find"
|
||||||
|
action:@selector(useSelectionForFind:)
|
||||||
|
keyEquivalent:@"e"]];
|
||||||
|
|
||||||
[menu setSubmenu:submenu];
|
[menu setSubmenu:submenu];
|
||||||
return menu;
|
return menu;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* 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
|
||||||
*/
|
*/
|
||||||
|
@ -60,6 +60,10 @@
|
||||||
|
|
||||||
- (void)setPreferredColorScheme:(Web::CSS::PreferredColorScheme)color_scheme;
|
- (void)setPreferredColorScheme:(Web::CSS::PreferredColorScheme)color_scheme;
|
||||||
|
|
||||||
|
- (void)findInPage:(NSString*)query;
|
||||||
|
- (void)findInPageNextMatch;
|
||||||
|
- (void)findInPagePreviousMatch;
|
||||||
|
|
||||||
- (void)zoomIn;
|
- (void)zoomIn;
|
||||||
- (void)zoomOut;
|
- (void)zoomOut;
|
||||||
- (void)resetZoom;
|
- (void)resetZoom;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* 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
|
||||||
*/
|
*/
|
||||||
|
@ -180,6 +180,21 @@ struct HideCursor {
|
||||||
m_web_view_bridge->set_system_visibility_state(is_visible);
|
m_web_view_bridge->set_system_visibility_state(is_visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)findInPage:(NSString*)query
|
||||||
|
{
|
||||||
|
m_web_view_bridge->find_in_page(Ladybird::ns_string_to_string(query));
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)findInPageNextMatch
|
||||||
|
{
|
||||||
|
m_web_view_bridge->find_in_page_next_match();
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)findInPagePreviousMatch
|
||||||
|
{
|
||||||
|
m_web_view_bridge->find_in_page_previous_match();
|
||||||
|
}
|
||||||
|
|
||||||
- (void)zoomIn
|
- (void)zoomIn
|
||||||
{
|
{
|
||||||
m_web_view_bridge->zoom_in();
|
m_web_view_bridge->zoom_in();
|
||||||
|
|
18
Ladybird/AppKit/UI/SearchPanel.h
Normal file
18
Ladybird/AppKit/UI/SearchPanel.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#import <System/Cocoa.h>
|
||||||
|
|
||||||
|
@interface SearchPanel : NSStackView
|
||||||
|
|
||||||
|
- (void)find:(id)selector;
|
||||||
|
- (void)findNextMatch:(id)selector;
|
||||||
|
- (void)findPreviousMatch:(id)selector;
|
||||||
|
- (void)useSelectionForFind:(id)selector;
|
||||||
|
|
||||||
|
@end
|
182
Ladybird/AppKit/UI/SearchPanel.mm
Normal file
182
Ladybird/AppKit/UI/SearchPanel.mm
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <UI/LadybirdWebViewBridge.h>
|
||||||
|
|
||||||
|
#import <UI/LadybirdWebView.h>
|
||||||
|
#import <UI/SearchPanel.h>
|
||||||
|
#import <UI/Tab.h>
|
||||||
|
#import <Utilities/Conversions.h>
|
||||||
|
|
||||||
|
#if !__has_feature(objc_arc)
|
||||||
|
# error "This project requires ARC"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static constexpr CGFloat const SEARCH_FIELD_HEIGHT = 30;
|
||||||
|
static constexpr CGFloat const SEARCH_FIELD_WIDTH = 300;
|
||||||
|
|
||||||
|
@interface SearchPanel () <NSSearchFieldDelegate>
|
||||||
|
|
||||||
|
@property (nonatomic, strong) NSSearchField* search_field;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation SearchPanel
|
||||||
|
|
||||||
|
- (instancetype)init
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
self.search_field = [[NSSearchField alloc] init];
|
||||||
|
[self.search_field setPlaceholderString:@"Search"];
|
||||||
|
[self.search_field setDelegate:self];
|
||||||
|
|
||||||
|
auto* search_previous = [NSButton buttonWithImage:[NSImage imageNamed:NSImageNameGoLeftTemplate]
|
||||||
|
target:self
|
||||||
|
action:@selector(findPreviousMatch:)];
|
||||||
|
[search_previous setToolTip:@"Find Previous Match"];
|
||||||
|
[search_previous setBordered:NO];
|
||||||
|
|
||||||
|
auto* search_next = [NSButton buttonWithImage:[NSImage imageNamed:NSImageNameGoRightTemplate]
|
||||||
|
target:self
|
||||||
|
action:@selector(findNextMatch:)];
|
||||||
|
[search_next setToolTip:@"Find Next Match"];
|
||||||
|
[search_next setBordered:NO];
|
||||||
|
|
||||||
|
auto* search_done = [NSButton buttonWithTitle:@"Done"
|
||||||
|
target:self
|
||||||
|
action:@selector(cancelSearch:)];
|
||||||
|
[search_done setToolTip:@"Close Search Bar"];
|
||||||
|
[search_done setBezelStyle:NSBezelStyleAccessoryBarAction];
|
||||||
|
|
||||||
|
[self addView:self.search_field inGravity:NSStackViewGravityLeading];
|
||||||
|
[self addView:search_previous inGravity:NSStackViewGravityLeading];
|
||||||
|
[self addView:search_next inGravity:NSStackViewGravityLeading];
|
||||||
|
[self addView:search_done inGravity:NSStackViewGravityTrailing];
|
||||||
|
|
||||||
|
[self setOrientation:NSUserInterfaceLayoutOrientationHorizontal];
|
||||||
|
[self setEdgeInsets:NSEdgeInsets { 0, 8, 0, 8 }];
|
||||||
|
|
||||||
|
[[self heightAnchor] constraintEqualToConstant:SEARCH_FIELD_HEIGHT].active = YES;
|
||||||
|
[[self.search_field widthAnchor] constraintEqualToConstant:SEARCH_FIELD_WIDTH].active = YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Public methods
|
||||||
|
|
||||||
|
- (void)find:(id)sender
|
||||||
|
{
|
||||||
|
[self setHidden:NO];
|
||||||
|
[self setSearchTextFromPasteBoard];
|
||||||
|
|
||||||
|
[self.window makeFirstResponder:self.search_field];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)findNextMatch:(id)sender
|
||||||
|
{
|
||||||
|
if ([self setSearchTextFromPasteBoard]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[[self tab] web_view] findInPageNextMatch];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)findPreviousMatch:(id)sender
|
||||||
|
{
|
||||||
|
if ([self setSearchTextFromPasteBoard]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[[self tab] web_view] findInPagePreviousMatch];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)useSelectionForFind:(id)sender
|
||||||
|
{
|
||||||
|
auto selected_text = [[[self tab] web_view] view].selected_text();
|
||||||
|
auto* query = Ladybird::string_to_ns_string(selected_text);
|
||||||
|
|
||||||
|
[self setPasteBoardContents:query];
|
||||||
|
|
||||||
|
if (![self isHidden]) {
|
||||||
|
[self.search_field setStringValue:query];
|
||||||
|
[[[self tab] web_view] findInPage:query];
|
||||||
|
|
||||||
|
[self.window makeFirstResponder:self.search_field];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Private methods
|
||||||
|
|
||||||
|
- (Tab*)tab
|
||||||
|
{
|
||||||
|
return (Tab*)[self window];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setPasteBoardContents:(NSString*)query
|
||||||
|
{
|
||||||
|
auto* paste_board = [NSPasteboard pasteboardWithName:NSPasteboardNameFind];
|
||||||
|
[paste_board clearContents];
|
||||||
|
[paste_board setString:query forType:NSPasteboardTypeString];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)setSearchTextFromPasteBoard
|
||||||
|
{
|
||||||
|
auto* paste_board = [NSPasteboard pasteboardWithName:NSPasteboardNameFind];
|
||||||
|
auto* query = [paste_board stringForType:NSPasteboardTypeString];
|
||||||
|
|
||||||
|
if (query) {
|
||||||
|
if (![[self.search_field stringValue] isEqual:query]) {
|
||||||
|
[self.search_field setStringValue:query];
|
||||||
|
[[[self tab] web_view] findInPage:query];
|
||||||
|
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)cancelSearch:(id)sender
|
||||||
|
{
|
||||||
|
[self setHidden:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - NSSearchFieldDelegate
|
||||||
|
|
||||||
|
- (void)controlTextDidChange:(NSNotification*)notification
|
||||||
|
{
|
||||||
|
auto* query = [self.search_field stringValue];
|
||||||
|
[[[self tab] web_view] findInPage:query];
|
||||||
|
|
||||||
|
[self setPasteBoardContents:query];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)control:(NSControl*)control
|
||||||
|
textView:(NSTextView*)text_view
|
||||||
|
doCommandBySelector:(SEL)selector
|
||||||
|
{
|
||||||
|
if (selector == @selector(insertNewline:)) {
|
||||||
|
NSEvent* event = [[self tab] currentEvent];
|
||||||
|
|
||||||
|
if ((event.modifierFlags & NSEventModifierFlagShift) == 0) {
|
||||||
|
[self findNextMatch:nil];
|
||||||
|
} else {
|
||||||
|
[self findPreviousMatch:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selector == @selector(cancelOperation:)) {
|
||||||
|
[self cancelSearch:nil];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* 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
|
||||||
*/
|
*/
|
||||||
|
@ -17,6 +17,7 @@
|
||||||
#import <UI/Inspector.h>
|
#import <UI/Inspector.h>
|
||||||
#import <UI/InspectorController.h>
|
#import <UI/InspectorController.h>
|
||||||
#import <UI/LadybirdWebView.h>
|
#import <UI/LadybirdWebView.h>
|
||||||
|
#import <UI/SearchPanel.h>
|
||||||
#import <UI/Tab.h>
|
#import <UI/Tab.h>
|
||||||
#import <UI/TabController.h>
|
#import <UI/TabController.h>
|
||||||
#import <Utilities/Conversions.h>
|
#import <Utilities/Conversions.h>
|
||||||
|
@ -33,6 +34,8 @@ static constexpr CGFloat const WINDOW_HEIGHT = 800;
|
||||||
@property (nonatomic, strong) NSString* title;
|
@property (nonatomic, strong) NSString* title;
|
||||||
@property (nonatomic, strong) NSImage* favicon;
|
@property (nonatomic, strong) NSImage* favicon;
|
||||||
|
|
||||||
|
@property (nonatomic, strong) SearchPanel* search_panel;
|
||||||
|
|
||||||
@property (nonatomic, strong) InspectorController* inspector_controller;
|
@property (nonatomic, strong) InspectorController* inspector_controller;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -84,7 +87,10 @@ static constexpr CGFloat const WINDOW_HEIGHT = 800;
|
||||||
[self setTitleVisibility:NSWindowTitleHidden];
|
[self setTitleVisibility:NSWindowTitleHidden];
|
||||||
[self setIsVisible:YES];
|
[self setIsVisible:YES];
|
||||||
|
|
||||||
auto* scroll_view = [[NSScrollView alloc] initWithFrame:[self frame]];
|
self.search_panel = [[SearchPanel alloc] init];
|
||||||
|
[self.search_panel setHidden:YES];
|
||||||
|
|
||||||
|
auto* scroll_view = [[NSScrollView alloc] init];
|
||||||
[scroll_view setHasVerticalScroller:YES];
|
[scroll_view setHasVerticalScroller:YES];
|
||||||
[scroll_view setHasHorizontalScroller:YES];
|
[scroll_view setHasHorizontalScroller:YES];
|
||||||
[scroll_view setLineScroll:24];
|
[scroll_view setLineScroll:24];
|
||||||
|
@ -92,13 +98,23 @@ static constexpr CGFloat const WINDOW_HEIGHT = 800;
|
||||||
[scroll_view setContentView:self.web_view];
|
[scroll_view setContentView:self.web_view];
|
||||||
[scroll_view setDocumentView:[[NSView alloc] init]];
|
[scroll_view setDocumentView:[[NSView alloc] init]];
|
||||||
|
|
||||||
|
auto* stack_view = [NSStackView stackViewWithViews:@[
|
||||||
|
self.search_panel,
|
||||||
|
scroll_view,
|
||||||
|
]];
|
||||||
|
|
||||||
|
[stack_view setOrientation:NSUserInterfaceLayoutOrientationVertical];
|
||||||
|
[stack_view setSpacing:0];
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter]
|
[[NSNotificationCenter defaultCenter]
|
||||||
addObserver:self
|
addObserver:self
|
||||||
selector:@selector(onContentScroll:)
|
selector:@selector(onContentScroll:)
|
||||||
name:NSViewBoundsDidChangeNotification
|
name:NSViewBoundsDidChangeNotification
|
||||||
object:[scroll_view contentView]];
|
object:[scroll_view contentView]];
|
||||||
|
|
||||||
[self setContentView:scroll_view];
|
[self setContentView:stack_view];
|
||||||
|
|
||||||
|
[[self.search_panel leadingAnchor] constraintEqualToAnchor:[self.contentView leadingAnchor]].active = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
@ -106,6 +122,26 @@ static constexpr CGFloat const WINDOW_HEIGHT = 800;
|
||||||
|
|
||||||
#pragma mark - Public methods
|
#pragma mark - Public methods
|
||||||
|
|
||||||
|
- (void)find:(id)sender
|
||||||
|
{
|
||||||
|
[self.search_panel find:sender];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)findNextMatch:(id)sender
|
||||||
|
{
|
||||||
|
[self.search_panel findNextMatch:sender];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)findPreviousMatch:(id)sender
|
||||||
|
{
|
||||||
|
[self.search_panel findPreviousMatch:sender];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)useSelectionForFind:(id)sender
|
||||||
|
{
|
||||||
|
[self.search_panel useSelectionForFind:sender];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)tabWillClose
|
- (void)tabWillClose
|
||||||
{
|
{
|
||||||
if (self.inspector_controller != nil) {
|
if (self.inspector_controller != nil) {
|
||||||
|
|
|
@ -145,6 +145,7 @@ elseif (APPLE)
|
||||||
AppKit/UI/LadybirdWebView.mm
|
AppKit/UI/LadybirdWebView.mm
|
||||||
AppKit/UI/LadybirdWebViewBridge.cpp
|
AppKit/UI/LadybirdWebViewBridge.cpp
|
||||||
AppKit/UI/Palette.mm
|
AppKit/UI/Palette.mm
|
||||||
|
AppKit/UI/SearchPanel.mm
|
||||||
AppKit/UI/Tab.mm
|
AppKit/UI/Tab.mm
|
||||||
AppKit/UI/TabController.mm
|
AppKit/UI/TabController.mm
|
||||||
AppKit/UI/TaskManager.mm
|
AppKit/UI/TaskManager.mm
|
||||||
|
|
|
@ -126,6 +126,7 @@ executable("ladybird_executable") {
|
||||||
"AppKit/UI/LadybirdWebView.mm",
|
"AppKit/UI/LadybirdWebView.mm",
|
||||||
"AppKit/UI/LadybirdWebViewBridge.cpp",
|
"AppKit/UI/LadybirdWebViewBridge.cpp",
|
||||||
"AppKit/UI/Palette.mm",
|
"AppKit/UI/Palette.mm",
|
||||||
|
"AppKit/UI/SearchPanel.mm",
|
||||||
"AppKit/UI/Tab.mm",
|
"AppKit/UI/Tab.mm",
|
||||||
"AppKit/UI/TabController.mm",
|
"AppKit/UI/TabController.mm",
|
||||||
"AppKit/UI/TaskManager.mm",
|
"AppKit/UI/TaskManager.mm",
|
||||||
|
|
Loading…
Reference in a new issue