| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef UI_VIEWS_ACTION_VIEW_CONTROLLER_H_ |
| #define UI_VIEWS_ACTION_VIEW_CONTROLLER_H_ |
| |
| #include <map> |
| #include <memory> |
| #include <utility> |
| |
| #include "base/callback_list.h" |
| #include "base/memory/weak_ptr.h" |
| #include "ui/actions/actions.h" |
| #include "ui/views/view_tracker.h" |
| #include "ui/views/views_export.h" |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // To allow ActionViewController to support a new view class, implement an |
| // ActionViewInterface class for the view class. See action_view_interface.h. |
| // ui/views/controls/button/button.* has a concrete example. |
| namespace views { |
| |
| class View; |
| |
| // ActionViewControllerBase provides a base class for the templated |
| // ActionViewControllerTemplate. |
| class VIEWS_EXPORT ActionViewControllerBase { |
| public: |
| ActionViewControllerBase() = default; |
| ActionViewControllerBase(const ActionViewControllerBase&) = delete; |
| ActionViewControllerBase& operator=(const ActionViewControllerBase&) = delete; |
| virtual ~ActionViewControllerBase() = default; |
| }; |
| |
| // ActionViewControllerTemplate is the templated core functionality that manages |
| // the relationship between the action item and the view. The template allows |
| // the action view controller to be generalized to any view class. |
| template <typename ViewT> |
| class VIEWS_EXPORT ActionViewControllerTemplate |
| : public ActionViewControllerBase { |
| public: |
| ActionViewControllerTemplate() = default; |
| ActionViewControllerTemplate(ViewT* view, |
| base::WeakPtr<actions::ActionItem> action_item) { |
| SetActionView(view); |
| SetActionItem(action_item); |
| } |
| explicit ActionViewControllerTemplate(ViewT* view) { SetActionView(view); } |
| ActionViewControllerTemplate(const ActionViewControllerTemplate&) = delete; |
| ActionViewControllerTemplate& operator=(const ActionViewControllerTemplate&) = |
| delete; |
| ~ActionViewControllerTemplate() override = default; |
| |
| void ActionItemChanged() { |
| ViewT* action_view = GetActionView(); |
| actions::ActionItem* action_item = GetActionItem(); |
| if (!action_view || !action_item) { |
| return; |
| } |
| action_view->GetActionViewInterface()->ActionItemChangedImpl(action_item); |
| } |
| |
| void SetActionItem(base::WeakPtr<actions::ActionItem> action_item) { |
| if (GetActionItem() == action_item.get()) { |
| return; |
| } |
| action_item_ = action_item; |
| action_changed_subscription_ = {}; |
| LinkActionItemAndView(); |
| } |
| |
| void LinkActionItemAndView() { |
| ViewT* action_view = GetActionView(); |
| actions::ActionItem* action_item = GetActionItem(); |
| if (!action_item || !action_view) { |
| return; |
| } |
| action_changed_subscription_ = |
| action_item->AddActionChangedCallback(base::BindRepeating( |
| &ActionViewControllerTemplate<ViewT>::ActionItemChanged, |
| base::Unretained(this))); |
| ActionItemChanged(); |
| action_view->InvalidateLayout(); |
| action_view->SchedulePaint(); |
| } |
| |
| void TriggerAction() { |
| actions::ActionItem* action_item = GetActionItem(); |
| if (action_item) { |
| action_item->InvokeAction(); |
| } |
| } |
| |
| ViewT* GetActionView() { |
| return static_cast<ViewT*>(action_view_tracker_.view()); |
| } |
| |
| void SetActionView(ViewT* action_view) { |
| if (GetActionView() == action_view) { |
| return; |
| } |
| action_view_tracker_.SetView(action_view); |
| // base::Unretained is okay because by view controller patterns, view |
| // controllers must outlive the views they manage. |
| action_view->GetActionViewInterface()->LinkActionTriggerToView( |
| base::BindRepeating(&ActionViewControllerTemplate::TriggerAction, |
| base::Unretained(this))); |
| LinkActionItemAndView(); |
| } |
| |
| actions::ActionItem* GetActionItem() { return action_item_.get(); } |
| |
| private: |
| views::ViewTracker action_view_tracker_; |
| base::WeakPtr<actions::ActionItem> action_item_ = nullptr; |
| base::CallbackListSubscription action_changed_subscription_; |
| }; |
| |
| // ActionViewController is the main view controller to be instantiated or |
| // subclassed. It should outlive all the views it manages. Under the hood it |
| // creates the appropriate templated ActionViewControllerTemplate for all |
| // classes of views. |
| class VIEWS_EXPORT ActionViewController { |
| public: |
| ActionViewController(); |
| ActionViewController(const ActionViewController&) = delete; |
| ActionViewController& operator=(const ActionViewController&) = delete; |
| virtual ~ActionViewController(); |
| |
| template <typename ViewT> |
| void CreateActionViewRelationship( |
| ViewT* view, |
| base::WeakPtr<actions::ActionItem> action_item) { |
| std::unique_ptr<ActionViewControllerTemplate<ViewT>> controller = |
| std::make_unique<ActionViewControllerTemplate<ViewT>>(view, |
| action_item); |
| action_view_controller_templates_[view] = std::move(controller); |
| } |
| |
| std::map<View*, std::unique_ptr<ActionViewControllerBase>> |
| action_view_controller_templates_; |
| }; |
| |
| } // namespace views |
| |
| #endif // UI_VIEWS_ACTION_VIEW_CONTROLLER_H_ |