Skip to content

Commit

Permalink
Add copy clean link to the macOS application menu bar
Browse files Browse the repository at this point in the history
  • Loading branch information
spylogsster committed Dec 13, 2022
1 parent 425992a commit 96baed4
Show file tree
Hide file tree
Showing 15 changed files with 343 additions and 3 deletions.
25 changes: 25 additions & 0 deletions browser/brave_app_controller_mac.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* Copyright (c) 2022 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#ifndef BRAVE_BROWSER_BRAVE_APP_CONTROLLER_MAC_H_
#define BRAVE_BROWSER_BRAVE_APP_CONTROLLER_MAC_H_

#import "chrome/browser/app_controller_mac.h"

// Manages logic to switch hotkey between copy and copy clean link item.
@interface BraveAppController : AppController {
NSMenuItem* _copyMenuItem;
NSMenuItem* _copyCleanLinkMenuItem;
absl::optional<bool> _hasSelectedURLForTesting;
}

// Testing API.
- (void)setCopyMenuItemForTesting:(NSMenuItem*)menuItem; // NOLINT
- (void)setCopyCleanLinkMenuItemForTesting:(NSMenuItem*)menuItem; // NOLINT
- (void)setSelectedURLForTesting:(bool)selected; // NOLINT

@end

#endif // BRAVE_BROWSER_BRAVE_APP_CONTROLLER_MAC_H_
103 changes: 103 additions & 0 deletions browser/brave_app_controller_mac.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/* Copyright (c) 2022 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#import "brave/browser/brave_app_controller_mac.h"

#include "brave/app/brave_command_ids.h"
#include "brave/browser/ui/browser_commands.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/grit/generated_resources.h"

@implementation BraveAppController

- (void)mainMenuCreated {
[super mainMenuCreated];

NSMenu* editMenu = [[[NSApp mainMenu] itemWithTag:IDC_EDIT_MENU] submenu];
_copyMenuItem = [editMenu itemWithTag:IDC_CONTENT_CONTEXT_COPY];
DCHECK(_copyMenuItem);
[[_copyMenuItem menu] setDelegate:self];
_copyCleanLinkMenuItem = [editMenu itemWithTag:IDC_COPY_CLEAN_LINK];
DCHECK(_copyCleanLinkMenuItem);
[[_copyCleanLinkMenuItem menu] setDelegate:self];
}

- (void)dealloc {
[[_copyMenuItem menu] setDelegate:nil];
[[_copyCleanLinkMenuItem menu] setDelegate:nil];
[super dealloc];
}

- (Browser*)getBrowser {
return chrome::FindBrowserWithProfile([self lastProfileIfLoaded]);
}

- (BOOL)shouldShowCleanLinkItem {
if (_hasSelectedURLForTesting.has_value()) {
return _hasSelectedURLForTesting.value();
}
return brave::HasSelectedURL([self getBrowser]);
}

- (void)setKeyEquivalentToItem:(NSMenuItem*)item {
auto* hootkeyItem =
item == _copyMenuItem ? _copyMenuItem : _copyCleanLinkMenuItem;
auto* noHootkeyItem =
item == _copyMenuItem ? _copyCleanLinkMenuItem : _copyMenuItem;

[hootkeyItem setKeyEquivalent:@"c"];
[hootkeyItem setKeyEquivalentModifierMask:NSEventModifierFlagCommand];

[noHootkeyItem setKeyEquivalent:@""];
[noHootkeyItem setKeyEquivalentModifierMask:0];
}

- (void)menuNeedsUpdate:(NSMenu*)menu {
if (menu != [_copyMenuItem menu] && menu != [_copyCleanLinkMenuItem menu]) {
[super menuNeedsUpdate:menu];
return;
}
if ([self shouldShowCleanLinkItem]) {
[_copyCleanLinkMenuItem setHidden:NO];
[self setKeyEquivalentToItem:_copyCleanLinkMenuItem];
} else {
[_copyCleanLinkMenuItem setHidden:YES];
[self setKeyEquivalentToItem:_copyMenuItem];
}
}

- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
NSInteger tag = [item tag];
if (tag == IDC_COPY_CLEAN_LINK) {
return [self shouldShowCleanLinkItem];
}
return [super validateUserInterfaceItem:item];
}

- (void)commandDispatch:(id)sender {
NSInteger tag = [sender tag];
if (tag == IDC_COPY_CLEAN_LINK) {
chrome::ExecuteCommand([self getBrowser], IDC_COPY_CLEAN_LINK);
return;
}

[super commandDispatch:sender];
}

- (void)setCopyMenuItemForTesting:(NSMenuItem*)menuItem {
_copyMenuItem = menuItem;
}

- (void)setCopyCleanLinkMenuItemForTesting:(NSMenuItem*)menuItem {
_copyCleanLinkMenuItem = menuItem;
}
- (void)setSelectedURLForTesting:(bool)value {
_hasSelectedURLForTesting = value;
}

@end // @implementation BraveAppController
77 changes: 77 additions & 0 deletions browser/brave_app_controller_mac_unittest.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/* Copyright (c) 2022 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#import <Cocoa/Cocoa.h>

#import "brave/browser/brave_app_controller_mac.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/platform_test.h"

class BraveAppControllerTest : public PlatformTest {
protected:
BraveAppControllerTest() {}

void SetUp() override {
PlatformTest::SetUp();
braveAppController_.reset([[BraveAppController alloc] init]);
copyMenuItem_.reset([[NSMenuItem alloc] initWithTitle:@""
action:0
keyEquivalent:@""]);
[braveAppController_ setCopyMenuItemForTesting:copyMenuItem_];

copyCleanLinkMenuItem_.reset([[NSMenuItem alloc] initWithTitle:@""
action:0
keyEquivalent:@""]);
[braveAppController_
setCopyCleanLinkMenuItemForTesting:copyCleanLinkMenuItem_];
}

void TearDown() override {
[braveAppController_ setCopyMenuItemForTesting:nil];
[braveAppController_ setCopyCleanLinkMenuItemForTesting:nil];
PlatformTest::TearDown();
}

void CheckHotkeysOnCopyItem() {
[braveAppController_ setSelectedURLForTesting:false];

[braveAppController_ menuNeedsUpdate:[copyMenuItem_ menu]];

EXPECT_TRUE([[copyMenuItem_ keyEquivalent] isEqualToString:@"c"]);
EXPECT_EQ([copyMenuItem_ keyEquivalentModifierMask],
NSEventModifierFlagCommand);

EXPECT_TRUE([[copyCleanLinkMenuItem_ keyEquivalent] isEqualToString:@""]);
EXPECT_EQ([copyCleanLinkMenuItem_ keyEquivalentModifierMask], 0UL);
EXPECT_TRUE([copyCleanLinkMenuItem_ isHidden]);
}

void CheckHotkeysOnCleanLinkItem() {
[braveAppController_ setSelectedURLForTesting:true];

[braveAppController_ menuNeedsUpdate:[copyMenuItem_ menu]];

EXPECT_TRUE([[copyMenuItem_ keyEquivalent] isEqualToString:@""]);
EXPECT_EQ([copyMenuItem_ keyEquivalentModifierMask], 0UL);

EXPECT_TRUE([[copyCleanLinkMenuItem_ keyEquivalent] isEqualToString:@"c"]);
EXPECT_EQ([copyCleanLinkMenuItem_ keyEquivalentModifierMask],
NSEventModifierFlagCommand);
EXPECT_FALSE([copyCleanLinkMenuItem_ isHidden]);
}

base::scoped_nsobject<BraveAppController> braveAppController_;
base::scoped_nsobject<NSMenuItem> copyMenuItem_;
base::scoped_nsobject<NSMenuItem> copyCleanLinkMenuItem_;
content::BrowserTaskEnvironment task_environment_;
};

TEST_F(BraveAppControllerTest, OnlyCopyItem) {
CheckHotkeysOnCopyItem();
}

TEST_F(BraveAppControllerTest, CleanLinkItemAdded) {
CheckHotkeysOnCleanLinkItem();
}
2 changes: 2 additions & 0 deletions browser/sources.gni
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,8 @@ brave_chrome_browser_deps = [

if (is_mac) {
brave_chrome_browser_sources += [
"//brave/browser/brave_app_controller_mac.h",
"//brave/browser/brave_app_controller_mac.mm",
"//brave/browser/brave_browser_main_parts_mac.h",
"//brave/browser/brave_browser_main_parts_mac.mm",
"//brave/browser/brave_shell_integration_mac.h",
Expand Down
2 changes: 2 additions & 0 deletions browser/ui/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ source_set("ui") {
"views/omnibox/brave_omnibox_popup_contents_view.h",
"views/omnibox/brave_omnibox_result_view.cc",
"views/omnibox/brave_omnibox_result_view.h",
"views/omnibox/brave_omnibox_view_views.cc",
"views/omnibox/brave_omnibox_view_views.h",
"views/omnibox/brave_rounded_omnibox_results_frame.cc",
"views/omnibox/brave_rounded_omnibox_results_frame.h",
"views/omnibox/brave_search_conversion_promotion_view.cc",
Expand Down
8 changes: 8 additions & 0 deletions browser/ui/browser_commands.cc
Original file line number Diff line number Diff line change
Expand Up @@ -277,4 +277,12 @@ void ToggleSidebarPosition(Browser* browser) {
!prefs->GetBoolean(prefs::kSidePanelHorizontalAlignment));
}

bool HasSelectedURL(Browser* browser) {
#if defined(TOOLKIT_VIEWS)
return static_cast<BraveBrowserView*>(browser->window())->HasSelectedURL();
#else
return false;
#endif
}

} // namespace brave
2 changes: 1 addition & 1 deletion browser/ui/browser_commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class Browser;
class GURL;

namespace brave {

bool HasSelectedURL(Browser* browser);
void NewOffTheRecordWindowTor(Browser*);
void NewTorConnectionForSite(Browser*);
void AddNewProfile();
Expand Down
7 changes: 7 additions & 0 deletions browser/ui/views/frame/brave_browser_view.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "brave/browser/ui/views/frame/vertical_tab_strip_region_view.h"
#include "brave/browser/ui/views/frame/vertical_tab_strip_widget_delegate_view.h"
#include "brave/browser/ui/views/location_bar/brave_location_bar_view.h"
#include "brave/browser/ui/views/omnibox/brave_omnibox_view_views.h"
#include "brave/browser/ui/views/sidebar/sidebar_container_view.h"
#include "brave/browser/ui/views/tabs/features.h"
#include "brave/browser/ui/views/toolbar/bookmark_button.h"
Expand Down Expand Up @@ -404,6 +405,12 @@ speedreader::SpeedreaderBubbleView* BraveBrowserView::ShowSpeedreaderBubble(
#endif
}

bool BraveBrowserView::HasSelectedURL() const {
return static_cast<BraveOmniboxViewViews*>(
GetLocationBarView()->omnibox_view())
->SelectedTextIsURL();
}

WalletButton* BraveBrowserView::GetWalletButton() {
return static_cast<BraveToolbarView*>(toolbar())->wallet_button();
}
Expand Down
5 changes: 3 additions & 2 deletions browser/ui/views/frame/brave_browser_view.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* Copyright 2019 The Brave Authors. All rights reserved.
/* Copyright (c) 2019 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#ifndef BRAVE_BROWSER_UI_VIEWS_FRAME_BRAVE_BROWSER_VIEW_H_
#define BRAVE_BROWSER_UI_VIEWS_FRAME_BRAVE_BROWSER_VIEW_H_
Expand Down Expand Up @@ -84,6 +84,7 @@ class BraveBrowserView : public BrowserView {

views::View* sidebar_host_view() { return sidebar_host_view_; }
bool IsSidebarVisible() const;
bool HasSelectedURL() const;

VerticalTabStripWidgetDelegateView*
vertical_tab_strip_widget_delegate_view() {
Expand Down
41 changes: 41 additions & 0 deletions browser/ui/views/omnibox/brave_omnibox_view_views.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* Copyright (c) 2022 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#include "brave/browser/ui/views/omnibox/brave_omnibox_view_views.h"

#include <utility>

#include "brave/app/brave_command_ids.h"
#include "brave/browser/url_sanitizer/url_sanitizer_service_factory.h"
#include "brave/components/url_sanitizer/browser/url_sanitizer_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
#include "chrome/grit/generated_resources.h"
#include "components/omnibox/browser/omnibox_edit_controller.h"
#include "components/omnibox/browser/omnibox_edit_model.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"

BraveOmniboxViewViews::BraveOmniboxViewViews(
OmniboxEditController* controller,
std::unique_ptr<OmniboxClient> client,
bool popup_window_mode,
LocationBarView* location_bar,
const gfx::FontList& font_list)
: OmniboxViewViews(controller,
std::move(client),
popup_window_mode,
location_bar,
font_list) {}

BraveOmniboxViewViews::~BraveOmniboxViewViews() = default;

bool BraveOmniboxViewViews::SelectedTextIsURL() {
GURL url;
bool write_url = false;
std::u16string selected_text = GetSelectedText();
model()->AdjustTextForCopy(GetSelectedRange().GetMin(), &selected_text, &url,
&write_url);
return write_url;
}
35 changes: 35 additions & 0 deletions browser/ui/views/omnibox/brave_omnibox_view_views.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* Copyright (c) 2022 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#ifndef BRAVE_BROWSER_UI_VIEWS_OMNIBOX_BRAVE_OMNIBOX_VIEW_VIEWS_H_
#define BRAVE_BROWSER_UI_VIEWS_OMNIBOX_BRAVE_OMNIBOX_VIEW_VIEWS_H_

#include <memory>

#include "chrome/browser/ui/views/omnibox/omnibox_view_views.h"

class OmniboxEditController;
class OmniboxClient;
class LocationBarView;
namespace gfx {
class FontList;
} // namespace gfx

class BraveOmniboxViewViews : public OmniboxViewViews {
public:
BraveOmniboxViewViews(OmniboxEditController* controller,
std::unique_ptr<OmniboxClient> client,
bool popup_window_mode,
LocationBarView* location_bar,
const gfx::FontList& font_list);

BraveOmniboxViewViews(const BraveOmniboxViewViews&) = delete;
BraveOmniboxViewViews& operator=(const BraveOmniboxViewViews&) = delete;
~BraveOmniboxViewViews() override;

bool SelectedTextIsURL();
};

#endif // BRAVE_BROWSER_UI_VIEWS_OMNIBOX_BRAVE_OMNIBOX_VIEW_VIEWS_H_
10 changes: 10 additions & 0 deletions chromium_src/chrome/browser/chrome_browser_main_mac.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* Copyright (c) 2022 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#include "chrome/browser/chrome_browser_main_mac.h"
#import "brave/browser/brave_app_controller_mac.h"

#define AppController BraveAppController
#include "src/chrome/browser/chrome_browser_main_mac.mm"
Loading

0 comments on commit 96baed4

Please sign in to comment.