blob: 1f770c85ed4e9cf4fb439d88a51a47137bf2bcc2 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_TOASTS_TOAST_VIEW_H_
#define CHROME_BROWSER_UI_TOASTS_TOAST_VIEW_H_
#include <memory>
#include <optional>
#include <string>
#include "base/functional/callback_forward.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
#include "ui/views/widget/widget.h"
namespace ui {
class MenuModel;
} // namespace ui
namespace views {
class ImageButton;
class ImageView;
class Label;
class MdTextButton;
class MenuModelAdapter;
class MenuRunner;
} // namespace views
namespace toasts {
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class ToastCloseReason {
kAutoDismissed = 0,
kActionButton = 1,
kCloseButton = 2,
kPreempted = 3,
kMenuItemClick = 4,
kFeatureDismiss = 5,
kAbort = 6,
kMaxValue = kAbort
};
// The view for toasts.
class ToastView : public views::BubbleDialogDelegateView,
public views::AnimationDelegateViews {
METADATA_HEADER(ToastView, views::BubbleDialogDelegateView)
public:
DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kToastViewId);
DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kToastActionButton);
DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kToastCloseButton);
DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kToastMenuButton);
ToastView(
views::View* anchor_view,
const std::u16string& toast_text,
const gfx::VectorIcon& icon,
const ui::ImageModel* image_override,
bool should_hide_ui_for_fullscreen,
base::RepeatingCallback<void(ToastCloseReason)> on_toast_close_callback);
~ToastView() override;
// Must be called prior to Init (which is called from
// views::BubbleDialogDelegateView::CreateBubble).
void AddActionButton(
const std::u16string& action_button_text,
base::RepeatingClosure action_button_callback = base::DoNothing());
// Must be called prior to Init (which is called from
// views::BubbleDialogDelegateView::CreateBubble).
void AddCloseButton(
base::RepeatingClosure close_button_callback = base::DoNothing());
// Adds a three-dot menu button that, when clicked, runs the menu defined by
// `model`. Must be called prior to `Init` (which is called from
// views::BubbleDialogDelegateView::CreateBubble).
void AddMenu(std::unique_ptr<ui::MenuModel> model);
// Adds the accelerator that runs `callback` when triggered. The accelerator
// is handled in this view when it has focus.
// Must be called prior to Init
// (which is called from views::BubbleDialogDelegateView::CreateBubble).
void AddAcceleratorCallback(ui::Accelerator accelerator,
base::RepeatingClosure callback);
// views::BubbleDialogDelegateView:
void Init() override;
// AnimationDelegateViews:
void AnimationProgressed(const gfx::Animation* animation) override;
void AnimateIn();
// Animates out the toast, then closes the toast widget.
void Close(ToastCloseReason close_reason);
void UpdateRenderToastOverWebContentsAndPaint(
const bool render_toast_over_web_contents);
views::Label* label_for_testing() { return label_; }
views::MdTextButton* action_button_for_testing() { return action_button_; }
views::ImageButton* close_button_for_testing() { return close_button_; }
bool is_animating_for_testing() const {
return height_animation_.is_animating();
}
// Gets the icon/image size from the layout provider.
static int GetIconSize();
protected:
// views::BubbleDialogDelegateView:
gfx::Rect GetBubbleBounds() override;
void OnThemeChanged() override;
bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
private:
void AnimateOut(base::OnceClosure callback, bool show_height_animation);
// Opens the menu if it is not already open or closes the existing menu
// otherwise. Assumes that `menu_model_` and `menu_button_` are not null.
void OnMenuButtonClicked();
// Resets `menu_runner_` and removes the highlight from the menu button.
void OnMenuClosed();
gfx::LinearAnimation height_animation_{this};
gfx::Rect starting_widget_bounds_;
gfx::Rect target_widget_bounds_;
gfx::Tween::Type height_animation_tween_;
const std::u16string toast_text_;
const raw_ref<const gfx::VectorIcon> icon_;
const raw_ptr<const ui::ImageModel> image_override_;
bool render_toast_over_web_contents_;
bool has_close_button_ = false;
bool has_action_button_ = false;
bool has_accelerator_ = false;
std::u16string action_button_text_;
base::RepeatingClosure action_button_callback_;
base::RepeatingClosure close_button_callback_;
base::RepeatingCallback<void(ToastCloseReason)> toast_close_callback_;
ui::Accelerator accelerator_;
base::RepeatingClosure accelerator_callback_;
std::unique_ptr<ui::MenuModel> menu_model_;
// Wraps `menu_model_` and triggers closing the toast after executing menu
// commands.
std::unique_ptr<views::MenuModelAdapter> menu_model_adapter_;
std::unique_ptr<views::MenuRunner> menu_runner_;
// Raw pointers to child views.
raw_ptr<views::Label> label_ = nullptr;
raw_ptr<views::ImageView> icon_view_ = nullptr;
raw_ptr<views::MdTextButton> action_button_ = nullptr;
raw_ptr<views::ImageButton> close_button_ = nullptr;
raw_ptr<views::ImageButton> menu_button_ = nullptr;
// If there was an attempt to close `this` while a menu was showing,
// `pending_close_reason_` contains the reason for closing the view and it
// is used to close the view once the menu stops showing.
std::optional<ToastCloseReason> pending_close_reason_;
};
} // namespace toasts
#endif // CHROME_BROWSER_UI_TOASTS_TOAST_VIEW_H_