diff --git a/browser/ui/BUILD.gn b/browser/ui/BUILD.gn index c8e8198a7c2a..3899da45e0a4 100644 --- a/browser/ui/BUILD.gn +++ b/browser/ui/BUILD.gn @@ -1050,6 +1050,10 @@ source_set("ui") { "views/brave_help_bubble/brave_help_bubble_delegate_view.h", "views/brave_help_bubble/brave_help_bubble_host_view.cc", "views/brave_help_bubble/brave_help_bubble_host_view.h", + "views/brave_news/brave_news_action_icon_view.cc", + "views/brave_news/brave_news_action_icon_view.h", + "views/brave_news/brave_news_bubble_controller.cc", + "views/brave_news/brave_news_bubble_controller.h", "views/frame/vertical_tab_strip_region_view.cc", "views/frame/vertical_tab_strip_region_view.h", "views/frame/vertical_tab_strip_root_view.cc", @@ -1058,8 +1062,6 @@ source_set("ui") { "views/frame/vertical_tab_strip_widget_delegate_view.h", "views/location_bar/brave_location_bar_view.cc", "views/location_bar/brave_location_bar_view.h", - "views/location_bar/brave_news_location_view.cc", - "views/location_bar/brave_news_location_view.h", "views/location_bar/brave_star_view.cc", "views/location_bar/brave_star_view.h", "views/profiles/brave_avatar_toolbar_button.cc", diff --git a/browser/ui/views/location_bar/brave_news_location_view.cc b/browser/ui/views/brave_news/brave_news_action_icon_view.cc similarity index 70% rename from browser/ui/views/location_bar/brave_news_location_view.cc rename to browser/ui/views/brave_news/brave_news_action_icon_view.cc index 0e8b390177e3..8c9a24f87e13 100644 --- a/browser/ui/views/location_bar/brave_news_location_view.cc +++ b/browser/ui/views/brave_news/brave_news_action_icon_view.cc @@ -1,9 +1,9 @@ -// Copyright (c) 2022 The Brave Authors. All rights reserved. +// Copyright (c) 2024 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/. -#include "brave/browser/ui/views/location_bar/brave_news_location_view.h" +#include "brave/browser/ui/views/brave_news/brave_news_action_icon_view.h" #include #include @@ -12,6 +12,7 @@ #include "base/functional/bind.h" #include "base/functional/callback_forward.h" #include "brave/browser/brave_news/brave_news_tab_helper.h" +#include "brave/browser/ui/views/brave_news/brave_news_bubble_controller.h" #include "brave/browser/ui/views/brave_news/brave_news_bubble_view.h" #include "brave/components/brave_news/common/pref_names.h" #include "brave/components/vector_icons/vector_icons.h" @@ -33,7 +34,7 @@ constexpr SkColor kSubscribedDarkColor = SkColorSetRGB(115, 122, 222); } // namespace -BraveNewsLocationView::BraveNewsLocationView( +BraveNewsActionIconView::BraveNewsActionIconView( Profile* profile, IconLabelBubbleView::Delegate* icon_label_bubble_delegate, PageActionIconView::Delegate* page_action_icon_delegate) @@ -46,26 +47,22 @@ BraveNewsLocationView::BraveNewsLocationView( should_show_.Init(brave_news::prefs::kShouldShowToolbarButton, profile->GetPrefs(), - base::BindRepeating(&BraveNewsLocationView::UpdateImpl, + base::BindRepeating(&BraveNewsActionIconView::UpdateImpl, base::Unretained(this))); opted_in_.Init(brave_news::prefs::kBraveNewsOptedIn, profile->GetPrefs(), - base::BindRepeating(&BraveNewsLocationView::UpdateImpl, + base::BindRepeating(&BraveNewsActionIconView::UpdateImpl, base::Unretained(this))); news_enabled_.Init(brave_news::prefs::kNewTabPageShowToday, profile->GetPrefs(), - base::BindRepeating(&BraveNewsLocationView::UpdateImpl, + base::BindRepeating(&BraveNewsActionIconView::UpdateImpl, base::Unretained(this))); Update(); } -BraveNewsLocationView::~BraveNewsLocationView() = default; +BraveNewsActionIconView::~BraveNewsActionIconView() = default; -views::BubbleDialogDelegate* BraveNewsLocationView::GetBubble() const { - return bubble_view_; -} - -void BraveNewsLocationView::UpdateImpl() { +void BraveNewsActionIconView::UpdateImpl() { auto* contents = GetWebContents(); BraveNewsTabHelper* tab_helper = contents ? BraveNewsTabHelper::FromWebContents(contents) : nullptr; @@ -114,30 +111,30 @@ void BraveNewsLocationView::UpdateImpl() { SetVisible(is_visible); } -void BraveNewsLocationView::WebContentsDestroyed() { +void BraveNewsActionIconView::WebContentsDestroyed() { page_feeds_observer_.Reset(); Observe(nullptr); } -const gfx::VectorIcon& BraveNewsLocationView::GetVectorIcon() const { +const gfx::VectorIcon& BraveNewsActionIconView::GetVectorIcon() const { return kLeoRssIcon; } -std::u16string BraveNewsLocationView::GetTextForTooltipAndAccessibleName() +std::u16string BraveNewsActionIconView::GetTextForTooltipAndAccessibleName() const { return l10n_util::GetStringUTF16(IDS_BRAVE_NEWS_ACTION_VIEW_TOOLTIP); } -bool BraveNewsLocationView::ShouldShowLabel() const { +bool BraveNewsActionIconView::ShouldShowLabel() const { return false; } -void BraveNewsLocationView::OnAvailableFeedsChanged( +void BraveNewsActionIconView::OnAvailableFeedsChanged( const std::vector& feeds) { Update(); } -void BraveNewsLocationView::OnThemeChanged() { +void BraveNewsActionIconView::OnThemeChanged() { bool subscribed = false; if (auto* contents = GetWebContents()) { subscribed = BraveNewsTabHelper::FromWebContents(contents)->IsSubscribed(); @@ -146,27 +143,12 @@ void BraveNewsLocationView::OnThemeChanged() { PageActionIconView::OnThemeChanged(); } -void BraveNewsLocationView::OnExecuting( +void BraveNewsActionIconView::OnExecuting( PageActionIconView::ExecuteSource execute_source) { - // If the bubble is already open, do nothing. - if (IsBubbleShowing()) { - return; - } - - auto* contents = GetWebContents(); - if (!contents) { - return; - } - - bubble_view_ = new BraveNewsBubbleView(this, contents); - bubble_view_->SetCloseCallback(base::BindOnce( - &BraveNewsLocationView::OnBubbleClosed, base::Unretained(this))); - auto* bubble_widget = - views::BubbleDialogDelegateView::CreateBubble(bubble_view_); - bubble_widget->Show(); + ShowBraveNewsBubble(); } -void BraveNewsLocationView::UpdateIconColor(bool subscribed) { +void BraveNewsActionIconView::UpdateIconColor(bool subscribed) { SkColor icon_color; if (subscribed) { auto is_dark = GetNativeTheme()->GetPreferredColorScheme() == @@ -178,9 +160,28 @@ void BraveNewsLocationView::UpdateIconColor(bool subscribed) { SetIconColor(icon_color); } -void BraveNewsLocationView::OnBubbleClosed() { - bubble_view_ = nullptr; +brave_news::BraveNewsBubbleController* BraveNewsActionIconView::GetController() + const { + auto* web_contents = GetWebContents(); + return web_contents ? brave_news::BraveNewsBubbleController:: + CreateOrGetFromWebContents(web_contents) + : nullptr; +} + +views::BubbleDialogDelegate* BraveNewsActionIconView::GetBubble() const { + auto* controller = GetController(); + return controller ? controller->GetBubble() : nullptr; +} + +void BraveNewsActionIconView::ShowBraveNewsBubble() { + if (auto* controller = GetController()) { + controller->ShowBubble(AsWeakPtr()); + } +} + +base::WeakPtr BraveNewsActionIconView::AsWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); } -BEGIN_METADATA(BraveNewsLocationView) +BEGIN_METADATA(BraveNewsActionIconView) END_METADATA diff --git a/browser/ui/views/location_bar/brave_news_location_view.h b/browser/ui/views/brave_news/brave_news_action_icon_view.h similarity index 61% rename from browser/ui/views/location_bar/brave_news_location_view.h rename to browser/ui/views/brave_news/brave_news_action_icon_view.h index 23214e4d1463..32303f125b3d 100644 --- a/browser/ui/views/location_bar/brave_news_location_view.h +++ b/browser/ui/views/brave_news/brave_news_action_icon_view.h @@ -1,10 +1,10 @@ -// Copyright (c) 2022 The Brave Authors. All rights reserved. +// Copyright (c) 2024 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_LOCATION_BAR_BRAVE_NEWS_LOCATION_VIEW_H_ -#define BRAVE_BROWSER_UI_VIEWS_LOCATION_BAR_BRAVE_NEWS_LOCATION_VIEW_H_ +#ifndef BRAVE_BROWSER_UI_VIEWS_BRAVE_NEWS_BRAVE_NEWS_ACTION_ICON_VIEW_H_ +#define BRAVE_BROWSER_UI_VIEWS_BRAVE_NEWS_BRAVE_NEWS_ACTION_ICON_VIEW_H_ #include #include @@ -18,22 +18,27 @@ #include "ui/views/view.h" class Profile; -class BraveNewsBubbleView; + +namespace brave_news { +class BraveNewsBubbleController; +} // LocationBar action for Brave News which shows a bubble allowing the user to // manage feed subscriptions for the current Tab -class BraveNewsLocationView : public PageActionIconView, - public BraveNewsTabHelper::PageFeedsObserver, - public content::WebContentsObserver { - METADATA_HEADER(BraveNewsLocationView, PageActionIconView) +class BraveNewsActionIconView : public PageActionIconView, + public BraveNewsTabHelper::PageFeedsObserver, + public content::WebContentsObserver { + METADATA_HEADER(BraveNewsActionIconView, PageActionIconView) public: - BraveNewsLocationView( + BraveNewsActionIconView( Profile* profile, IconLabelBubbleView::Delegate* icon_label_bubble_delegate, PageActionIconView::Delegate* page_action_icon_delegate); - BraveNewsLocationView(const BraveNewsLocationView&) = delete; - BraveNewsLocationView& operator=(const BraveNewsLocationView&) = delete; - ~BraveNewsLocationView() override; + BraveNewsActionIconView(const BraveNewsActionIconView&) = delete; + BraveNewsActionIconView& operator=(const BraveNewsActionIconView&) = delete; + ~BraveNewsActionIconView() override; + + base::WeakPtr AsWeakPtr(); // PageActionIconView: views::BubbleDialogDelegate* GetBubble() const override; @@ -51,13 +56,15 @@ class BraveNewsLocationView : public PageActionIconView, void WebContentsDestroyed() override; protected: + brave_news::BraveNewsBubbleController* GetController() const; + // PageActionIconView: void OnExecuting(PageActionIconView::ExecuteSource execute_source) override; const gfx::VectorIcon& GetVectorIcon() const override; private: void UpdateIconColor(bool subscribed); - void OnBubbleClosed(); + void ShowBraveNewsBubble(); base::ScopedObservation @@ -65,7 +72,8 @@ class BraveNewsLocationView : public PageActionIconView, BooleanPrefMember should_show_; BooleanPrefMember opted_in_; BooleanPrefMember news_enabled_; - raw_ptr bubble_view_ = nullptr; + + base::WeakPtrFactory weak_ptr_factory_{this}; }; -#endif // BRAVE_BROWSER_UI_VIEWS_LOCATION_BAR_BRAVE_NEWS_LOCATION_VIEW_H_ +#endif // BRAVE_BROWSER_UI_VIEWS_BRAVE_NEWS_BRAVE_NEWS_ACTION_ICON_VIEW_H_ diff --git a/browser/ui/views/brave_news/brave_news_bubble_controller.cc b/browser/ui/views/brave_news/brave_news_bubble_controller.cc new file mode 100644 index 000000000000..75abe2700c83 --- /dev/null +++ b/browser/ui/views/brave_news/brave_news_bubble_controller.cc @@ -0,0 +1,60 @@ +/* Copyright (c) 2024 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/brave_news/brave_news_bubble_controller.h" + +#include + +#include "base/memory/ptr_util.h" +#include "brave/browser/ui/views/brave_news/brave_news_action_icon_view.h" +#include "brave/browser/ui/views/brave_news/brave_news_bubble_view.h" +#include "ui/views/bubble/bubble_dialog_delegate_view.h" + +namespace brave_news { +// static +BraveNewsBubbleController* +BraveNewsBubbleController::CreateOrGetFromWebContents( + content::WebContents* web_contents) { + CHECK(web_contents); + BraveNewsBubbleController::CreateForWebContents(web_contents); + return BraveNewsBubbleController::FromWebContents(web_contents); +} + +BraveNewsBubbleController::~BraveNewsBubbleController() = default; + +void BraveNewsBubbleController::ShowBubble( + base::WeakPtr anchor_view) { + if (!anchor_view) { + return; + } + + bubble_ = new BraveNewsBubbleView(anchor_view.get(), web_contents_); + views::BubbleDialogDelegateView::CreateBubble( + base::WrapUnique( + static_cast(bubble_.get()))) + ->Show(); +} + +BraveNewsBubbleView* BraveNewsBubbleController::GetBubble() { + return bubble_; +} + +void BraveNewsBubbleController::OnBubbleClosed() { + bubble_ = nullptr; +} + +base::WeakPtr +BraveNewsBubbleController::AsWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} + +BraveNewsBubbleController::BraveNewsBubbleController( + content::WebContents* web_contents) + : content::WebContentsUserData(*web_contents), + web_contents_(web_contents) {} + +WEB_CONTENTS_USER_DATA_KEY_IMPL(BraveNewsBubbleController); + +} // namespace brave_news diff --git a/browser/ui/views/brave_news/brave_news_bubble_controller.h b/browser/ui/views/brave_news/brave_news_bubble_controller.h new file mode 100644 index 000000000000..0bb6c9301066 --- /dev/null +++ b/browser/ui/views/brave_news/brave_news_bubble_controller.h @@ -0,0 +1,49 @@ +/* Copyright (c) 2024 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_BRAVE_NEWS_BRAVE_NEWS_BUBBLE_CONTROLLER_H_ +#define BRAVE_BROWSER_UI_VIEWS_BRAVE_NEWS_BRAVE_NEWS_BUBBLE_CONTROLLER_H_ + +#include "base/memory/raw_ptr.h" +#include "base/memory/weak_ptr.h" +#include "content/public/browser/web_contents_user_data.h" + +namespace content { +class WebContents; +} + +class BraveNewsBubbleView; +class BraveNewsActionIconView; + +namespace brave_news { + +class BraveNewsBubbleController + : public content::WebContentsUserData { + public: + static BraveNewsBubbleController* CreateOrGetFromWebContents( + content::WebContents* web_contents); + + ~BraveNewsBubbleController() override; + + void ShowBubble(base::WeakPtr anchor_view); + BraveNewsBubbleView* GetBubble(); + void OnBubbleClosed(); + base::WeakPtr AsWeakPtr(); + + private: + friend class content::WebContentsUserData; + WEB_CONTENTS_USER_DATA_KEY_DECL(); + + explicit BraveNewsBubbleController(content::WebContents* web_contents); + + raw_ptr bubble_ = nullptr; + raw_ptr web_contents_ = nullptr; + + base::WeakPtrFactory weak_ptr_factory_{this}; +}; + +} // namespace brave_news + +#endif // BRAVE_BROWSER_UI_VIEWS_BRAVE_NEWS_BRAVE_NEWS_BUBBLE_CONTROLLER_H_ diff --git a/browser/ui/views/brave_news/brave_news_bubble_view.cc b/browser/ui/views/brave_news/brave_news_bubble_view.cc index 643ca0e58e20..f24d3f9fd0db 100644 --- a/browser/ui/views/brave_news/brave_news_bubble_view.cc +++ b/browser/ui/views/brave_news/brave_news_bubble_view.cc @@ -13,6 +13,7 @@ #include "base/strings/utf_string_conversions.h" #include "brave/browser/brave_news/brave_news_tab_helper.h" #include "brave/browser/themes/brave_dark_mode_utils.h" +#include "brave/browser/ui/views/brave_news/brave_news_bubble_controller.h" #include "brave/browser/ui/views/brave_news/brave_news_feed_item_view.h" #include "brave/browser/ui/views/brave_news/brave_news_feeds_container_view.h" #include "brave/components/brave_news/common/pref_names.h" @@ -77,6 +78,11 @@ BraveNewsBubbleView::BraveNewsBubbleView(views::View* action_view, contents_(contents) { DCHECK(contents); + auto* controller = + brave_news::BraveNewsBubbleController::FromWebContents(contents); + CHECK(controller); + controller_ = controller->AsWeakPtr(); + SetButtons(ui::DIALOG_BUTTON_NONE); SetAccessibleWindowRole(ax::mojom::Role::kDialog); set_adjust_if_offscreen(true); @@ -146,6 +152,14 @@ void BraveNewsBubbleView::OpenManageFeeds() { /*navigation_handle_callback=*/{}); } +void BraveNewsBubbleView::OnWidgetDestroyed(views::Widget*) { + if (!controller_) { + return; + } + + controller_->OnBubbleClosed(); +} + void BraveNewsBubbleView::OnThemeChanged() { views::BubbleDialogDelegateView::OnThemeChanged(); diff --git a/browser/ui/views/brave_news/brave_news_bubble_view.h b/browser/ui/views/brave_news/brave_news_bubble_view.h index 7a29a32a0c9d..a21e7bf8d8e5 100644 --- a/browser/ui/views/brave_news/brave_news_bubble_view.h +++ b/browser/ui/views/brave_news/brave_news_bubble_view.h @@ -16,6 +16,9 @@ #include "ui/views/widget/widget.h" class BraveNewsFeedsContainerView; +namespace brave_news { +class BraveNewsBubbleController; +} class BraveNewsBubbleView : public views::BubbleDialogDelegateView { METADATA_HEADER(BraveNewsBubbleView, views::BubbleDialogDelegateView) @@ -32,13 +35,16 @@ class BraveNewsBubbleView : public views::BubbleDialogDelegateView { void OpenManageFeeds(); // views::BubbleDialogDelegateView: + void OnWidgetDestroyed(views::Widget*) override; void OnThemeChanged() override; private: - raw_ptr contents_; + raw_ptr contents_ = nullptr; raw_ptr title_label_ = nullptr; raw_ptr subtitle_label_ = nullptr; raw_ptr feeds_container_ = nullptr; + + base::WeakPtr controller_; }; #endif // BRAVE_BROWSER_UI_VIEWS_BRAVE_NEWS_BRAVE_NEWS_BUBBLE_VIEW_H_ diff --git a/browser/ui/views/location_bar/brave_location_bar_view.cc b/browser/ui/views/location_bar/brave_location_bar_view.cc index fc52508ef969..aebba9fb603b 100644 --- a/browser/ui/views/location_bar/brave_location_bar_view.cc +++ b/browser/ui/views/location_bar/brave_location_bar_view.cc @@ -17,7 +17,7 @@ #include "brave/browser/ui/tabs/brave_tab_prefs.h" #include "brave/browser/ui/tabs/features.h" #include "brave/browser/ui/views/brave_actions/brave_actions_container.h" -#include "brave/browser/ui/views/location_bar/brave_news_location_view.h" +#include "brave/browser/ui/views/brave_news/brave_news_action_icon_view.h" #include "brave/browser/ui/views/playlist/playlist_action_icon_view.h" #include "brave/browser/ui/views/toolbar/brave_toolbar_view.h" #include "brave/components/commander/common/buildflags/buildflags.h" @@ -120,11 +120,11 @@ void BraveLocationBarView::Init() { } if (!browser_->profile()->IsOffTheRecord()) { - brave_news_location_view_ = - AddChildView(std::make_unique( + brave_news_action_icon_view_ = + AddChildView(std::make_unique( browser_->profile(), this, this)); - brave_news_location_view_->SetVisible(false); - views::InkDrop::Get(brave_news_location_view_) + brave_news_action_icon_view_->SetVisible(false); + views::InkDrop::Get(brave_news_action_icon_view_) ->SetVisibleOpacity(GetPageActionInkDropVisibleOpacity()); } #if BUILDFLAG(ENABLE_TOR) @@ -199,8 +199,8 @@ void BraveLocationBarView::Update(content::WebContents* contents) { } #endif - if (brave_news_location_view_) { - brave_news_location_view_->Update(); + if (brave_news_action_icon_view_) { + brave_news_action_icon_view_->Update(); } LocationBarView::Update(contents); @@ -263,8 +263,8 @@ void BraveLocationBarView::OnChanged() { } #endif - if (brave_news_location_view_) { - brave_news_location_view_->Update(); + if (brave_news_action_icon_view_) { + brave_news_action_icon_view_->Update(); } // OnChanged calls Layout @@ -273,8 +273,8 @@ void BraveLocationBarView::OnChanged() { std::vector BraveLocationBarView::GetTrailingViews() { std::vector views; - if (brave_news_location_view_) { - views.push_back(brave_news_location_view_); + if (brave_news_action_icon_view_) { + views.push_back(brave_news_action_icon_view_); } #if BUILDFLAG(ENABLE_TOR) if (onion_location_view_) { @@ -314,9 +314,11 @@ gfx::Size BraveLocationBarView::CalculatePreferredSize( brave_actions_min + GetLayoutConstant(LOCATION_BAR_ELEMENT_PADDING); min_size.Enlarge(extra_width, 0); } - if (brave_news_location_view_ && brave_news_location_view_->GetVisible()) { - const int extra_width = GetLayoutConstant(LOCATION_BAR_ELEMENT_PADDING) + - brave_news_location_view_->GetMinimumSize().width(); + if (brave_news_action_icon_view_ && + brave_news_action_icon_view_->GetVisible()) { + const int extra_width = + GetLayoutConstant(LOCATION_BAR_ELEMENT_PADDING) + + brave_news_action_icon_view_->GetMinimumSize().width(); min_size.Enlarge(extra_width, 0); } #if BUILDFLAG(ENABLE_TOR) diff --git a/browser/ui/views/location_bar/brave_location_bar_view.h b/browser/ui/views/location_bar/brave_location_bar_view.h index 9d0a9317cf3e..b71069dae513 100644 --- a/browser/ui/views/location_bar/brave_location_bar_view.h +++ b/browser/ui/views/location_bar/brave_location_bar_view.h @@ -10,7 +10,7 @@ #include #include "base/gtest_prod_util.h" -#include "brave/browser/ui/views/location_bar/brave_news_location_view.h" +#include "brave/browser/ui/views/brave_news/brave_news_action_icon_view.h" #include "brave/browser/ui/views/view_shadow.h" #include "brave/components/ipfs/buildflags/buildflags.h" #include "brave/components/tor/buildflags/buildflags.h" @@ -117,7 +117,7 @@ class BraveLocationBarView : public LocationBarView { std::unique_ptr shadow_; raw_ptr brave_actions_ = nullptr; - raw_ptr brave_news_location_view_ = nullptr; + raw_ptr brave_news_action_icon_view_ = nullptr; #if BUILDFLAG(ENABLE_TOR) raw_ptr onion_location_view_ = nullptr; #endif