#include "ui/views/window/dialog_delegate.h"
#include <utility>
#include "base/debug/alias.h"
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/observer_list.h"
#include "build/build_config.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/accessibility/ax_role_properties.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/gfx/color_palette.h"
#include "ui/strings/grit/ui_strings.h"
#include "ui/views/bubble/bubble_border.h"
#include "ui/views/bubble/bubble_frame_view.h"
#include "ui/views/buildflags.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/layout/layout_provider.h"
#include "ui/views/style/platform_style.h"
#include "ui/views/views_features.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_observer.h"
#include "ui/views/window/dialog_client_view.h"
#include "ui/views/window/dialog_observer.h"
namespace views {
int g_instance_count = 0;
DialogDelegate::Params::Params() = default;
DialogDelegate::Params::~Params() = default;
DialogDelegate::DialogDelegate() {
++g_instance_count;
WidgetDelegate::RegisterWindowWillCloseCallback(
base::BindOnce(&DialogDelegate::WindowWillClose, base::Unretained(this)));
}
Widget* DialogDelegate::CreateDialogWidget(WidgetDelegate* delegate,
gfx::NativeWindow context,
gfx::NativeView parent,
gfx::AcceleratedWidget parent_widget) {
views::Widget* widget = new views::Widget;
views::Widget::InitParams params =
GetDialogWidgetInitParams(delegate, context, parent, gfx::Rect(),
parent_widget);
widget->Init(std::move(params));
return widget;
}
Widget* DialogDelegate::CreateDialogWidget(
std::unique_ptr<WidgetDelegate> delegate,
gfx::NativeWindow context,
gfx::NativeView parent,
gfx::AcceleratedWidget parent_widget) {
DCHECK(delegate->owned_by_widget());
return CreateDialogWidget(delegate.release(), context, parent, parent_widget);
}
bool DialogDelegate::CanSupportCustomFrame(gfx::NativeView parent,
gfx::AcceleratedWidget parent_widget) {
#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && \
BUILDFLAG(ENABLE_DESKTOP_AURA)
return parent != nullptr || parent_widget != gfx::kNullAcceleratedWidget;
#else
return true;
#endif
}
Widget::InitParams DialogDelegate::GetDialogWidgetInitParams(
WidgetDelegate* delegate,
gfx::NativeWindow context,
gfx::NativeView parent,
const gfx::Rect& bounds,
gfx::AcceleratedWidget parent_widget) {
views::Widget::InitParams params;
params.delegate = delegate;
params.bounds = bounds;
DialogDelegate* dialog = delegate->AsDialogDelegate();
if (dialog)
dialog->params_.custom_frame &= CanSupportCustomFrame(parent, parent_widget);
if (!dialog || dialog->use_custom_frame()) {
params.opacity = Widget::InitParams::WindowOpacity::kTranslucent;
params.remove_standard_frame = true;
#if !BUILDFLAG(IS_MAC)
params.shadow_type = views::Widget::InitParams::ShadowType::kNone;
#endif
}
params.context = context;
params.parent = parent;
params.parent_widget = parent_widget;
#if !BUILDFLAG(IS_APPLE)
params.child = parent && (delegate->GetModalType() == ui::MODAL_TYPE_CHILD);
#endif
return params;
}
int DialogDelegate::GetDefaultDialogButton() const {
if (GetParams().default_button.has_value())
return *GetParams().default_button;
if (GetDialogButtons() & ui::DIALOG_BUTTON_OK)
return ui::DIALOG_BUTTON_OK;
if (GetDialogButtons() & ui::DIALOG_BUTTON_CANCEL)
return ui::DIALOG_BUTTON_CANCEL;
return ui::DIALOG_BUTTON_NONE;
}
std::u16string DialogDelegate::GetDialogButtonLabel(
ui::DialogButton button) const {
if (!GetParams().button_labels[button].empty())
return GetParams().button_labels[button];
if (button == ui::DIALOG_BUTTON_OK)
return l10n_util::GetStringUTF16(IDS_APP_OK);
CHECK_EQ(button, ui::DIALOG_BUTTON_CANCEL);
return GetDialogButtons() & ui::DIALOG_BUTTON_OK
? l10n_util::GetStringUTF16(IDS_APP_CANCEL)
: l10n_util::GetStringUTF16(IDS_APP_CLOSE);
}
bool DialogDelegate::IsDialogButtonEnabled(ui::DialogButton button) const {
return params_.enabled_buttons & button;
}
bool DialogDelegate::Cancel() {
DCHECK(!already_started_close_);
if (cancel_callback_)
RunCloseCallback(std::move(cancel_callback_));
return true;
}
bool DialogDelegate::Accept() {
DCHECK(!already_started_close_);
if (accept_callback_)
RunCloseCallback(std::move(accept_callback_));
return true;
}
void DialogDelegate::RunCloseCallback(base::OnceClosure callback) {
DCHECK(!already_started_close_);
already_started_close_ = true;
std::move(callback).Run();
}
View* DialogDelegate::GetInitiallyFocusedView() {
if (WidgetDelegate::HasConfiguredInitiallyFocusedView())
return WidgetDelegate::GetInitiallyFocusedView();
const DialogClientView* dcv = GetDialogClientView();
if (!dcv)
return nullptr;
int default_button = GetDefaultDialogButton();
if (default_button == ui::DIALOG_BUTTON_NONE)
return nullptr;
CHECK(default_button & GetDialogButtons());
if (default_button & ui::DIALOG_BUTTON_OK)
return dcv->ok_button();
if (default_button & ui::DIALOG_BUTTON_CANCEL)
return dcv->cancel_button();
return nullptr;
}
DialogDelegate* DialogDelegate::AsDialogDelegate() {
return this;
}
ClientView* DialogDelegate::CreateClientView(Widget* widget) {
return new DialogClientView(widget, TransferOwnershipOfContentsView());
}
std::unique_ptr<NonClientFrameView> DialogDelegate::CreateNonClientFrameView(
Widget* widget) {
return use_custom_frame() ? CreateDialogFrameView(widget)
: WidgetDelegate::CreateNonClientFrameView(widget);
}
void DialogDelegate::WindowWillClose() {
if (already_started_close_)
return;
bool new_callback_present =
close_callback_ || cancel_callback_ || accept_callback_;
if (close_callback_)
RunCloseCallback(std::move(close_callback_));
if (new_callback_present)
return;
already_started_close_ = true;
}
bool DialogDelegate::EscShouldCancelDialog() const {
return !close_callback_ && !ShouldShowCloseButton();
}
std::unique_ptr<NonClientFrameView> DialogDelegate::CreateDialogFrameView(
Widget* widget) {
LayoutProvider* provider = LayoutProvider::Get();
auto frame = std::make_unique<BubbleFrameView>(
provider->GetInsetsMetric(INSETS_DIALOG_TITLE), gfx::Insets());
const BubbleBorder::Shadow kShadow = BubbleBorder::DIALOG_SHADOW;
std::unique_ptr<BubbleBorder> border =
std::make_unique<BubbleBorder>(BubbleBorder::FLOAT, kShadow);
DialogDelegate* delegate = widget->widget_delegate()->AsDialogDelegate();
if (delegate) {
if (delegate->GetParams().round_corners)
border->SetCornerRadius(delegate->GetCornerRadius());
frame->SetFootnoteView(delegate->DisownFootnoteView());
}
frame->SetBubbleBorder(std::move(border));
return frame;
}
const DialogClientView* DialogDelegate::GetDialogClientView() const {
if (!GetWidget())
return nullptr;
const views::View* client_view = GetWidget()->client_view();
return client_view->GetClassName() == DialogClientView::kViewClassName
? static_cast<const DialogClientView*>(client_view)
: nullptr;
}
DialogClientView* DialogDelegate::GetDialogClientView() {
return const_cast<DialogClientView*>(
const_cast<const DialogDelegate*>(this)->GetDialogClientView());
}
BubbleFrameView* DialogDelegate::GetBubbleFrameView() const {
if (!use_custom_frame())
return nullptr;
const NonClientView* view =
GetWidget() ? GetWidget()->non_client_view() : nullptr;
return view ? static_cast<BubbleFrameView*>(view->frame_view()) : nullptr;
}
views::LabelButton* DialogDelegate::GetOkButton() const {
DCHECK(GetWidget()) << "Don't call this before OnWidgetInitialized";
auto* client = GetDialogClientView();
return client ? client->ok_button() : nullptr;
}
views::LabelButton* DialogDelegate::GetCancelButton() const {
DCHECK(GetWidget()) << "Don't call this before OnWidgetInitialized";
auto* client = GetDialogClientView();
return client ? client->cancel_button() : nullptr;
}
views::View* DialogDelegate::GetExtraView() const {
DCHECK(GetWidget()) << "Don't call this before OnWidgetInitialized";
auto* client = GetDialogClientView();
return client ? client->extra_view() : nullptr;
}
views::View* DialogDelegate::GetFootnoteViewForTesting() const {
if (!GetWidget())
return footnote_view_.get();
NonClientFrameView* frame = GetWidget()->non_client_view()->frame_view();
DCHECK_EQ(frame->GetClassName(), BubbleFrameView::kViewClassName);
return static_cast<BubbleFrameView*>(frame)->GetFootnoteView();
}
void DialogDelegate::AddObserver(DialogObserver* observer) {
observer_list_.AddObserver(observer);
}
void DialogDelegate::RemoveObserver(DialogObserver* observer) {
observer_list_.RemoveObserver(observer);
}
void DialogDelegate::DialogModelChanged() {
for (DialogObserver& observer : observer_list_)
observer.OnDialogChanged();
}
void DialogDelegate::TriggerInputProtection() {
GetDialogClientView()->TriggerInputProtection();
}
void DialogDelegate::SetDefaultButton(int button) {
if (params_.default_button == button)
return;
params_.default_button = button;
DialogModelChanged();
}
void DialogDelegate::SetButtons(int buttons) {
if (params_.buttons == buttons)
return;
params_.buttons = buttons;
DialogModelChanged();
}
void DialogDelegate::SetButtonEnabled(ui::DialogButton button, bool enabled) {
if (!!(params_.enabled_buttons & button) == enabled)
return;
if (enabled)
params_.enabled_buttons |= button;
else
params_.enabled_buttons &= ~button;
DialogModelChanged();
}
void DialogDelegate::SetButtonLabel(ui::DialogButton button,
std::u16string label) {
if (params_.button_labels[button] == label)
return;
params_.button_labels[button] = label;
DialogModelChanged();
}
void DialogDelegate::SetAcceptCallback(base::OnceClosure callback) {
accept_callback_ = std::move(callback);
}
void DialogDelegate::SetCancelCallback(base::OnceClosure callback) {
cancel_callback_ = std::move(callback);
}
void DialogDelegate::SetCloseCallback(base::OnceClosure callback) {
close_callback_ = std::move(callback);
}
std::unique_ptr<View> DialogDelegate::DisownExtraView() {
return std::move(extra_view_);
}
bool DialogDelegate::Close() {
WindowWillClose();
return true;
}
void DialogDelegate::ResetViewShownTimeStampForTesting() {
GetDialogClientView()->ResetViewShownTimeStampForTesting();
}
void DialogDelegate::SetButtonRowInsets(const gfx::Insets& insets) {
GetDialogClientView()->SetButtonRowInsets(insets);
}
void DialogDelegate::AcceptDialog() {
DEBUG_ALIAS_FOR_CSTR(last_widget_name, GetWidget()->GetName().c_str(), 64);
DCHECK(IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK));
if (already_started_close_ || !Accept())
return;
if (g_instance_count <= 0) {
LOG(FATAL) << last_widget_name;
}
already_started_close_ = true;
GetWidget()->CloseWithReason(
views::Widget::ClosedReason::kAcceptButtonClicked);
}
void DialogDelegate::CancelDialog() {
if (ShouldShowCloseButton())
DCHECK(IsDialogButtonEnabled(ui::DIALOG_BUTTON_CANCEL));
if (already_started_close_ || !Cancel())
return;
already_started_close_ = true;
GetWidget()->CloseWithReason(
views::Widget::ClosedReason::kCancelButtonClicked);
}
DialogDelegate::~DialogDelegate() {
--g_instance_count;
}
ax::mojom::Role DialogDelegate::GetAccessibleWindowRole() {
return ax::mojom::Role::kDialog;
}
int DialogDelegate::GetCornerRadius() const {
#if BUILDFLAG(IS_MAC)
if (GetModalType() == ui::MODAL_TYPE_WINDOW)
return 2;
#endif
if (params_.corner_radius)
return *params_.corner_radius;
return LayoutProvider::Get()->GetCornerRadiusMetric(
views::ShapeContextTokens::kDialogRadius);
}
std::unique_ptr<View> DialogDelegate::DisownFootnoteView() {
return std::move(footnote_view_);
}
DialogDelegateView::DialogDelegateView() {
SetOwnedByWidget(true);
}
DialogDelegateView::~DialogDelegateView() = default;
Widget* DialogDelegateView::GetWidget() {
return View::GetWidget();
}
const Widget* DialogDelegateView::GetWidget() const {
return View::GetWidget();
}
View* DialogDelegateView::GetContentsView() {
return this;
}
BEGIN_METADATA(DialogDelegateView, View)
END_METADATA
}