Commit ea7508e2 authored by Peter Boström's avatar Peter Boström Committed by Commit Bot

Use DialogModel for ExtensionUninstallDialogView

Adds the following to DialogModel:
* Support for dialog icons through DialogModel::Builder::SetIcon.
* Support for base::string16 (not message_id) in DialogModelLabel.
* Support for character break in DialogModelLabel.

Then refactors ExtensionUninstallDialogView to use DialogModel, which
required adding the above items to DialogModel.

Bug: 1106422
Change-Id: I59267cf6037f907171f9ff5121c37528b1d1a371
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2454978
Commit-Queue: Peter Boström <pbos@chromium.org>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Cr-Commit-Position: refs/heads/master@{#816711}
parent 6574db9c
...@@ -73,6 +73,7 @@ ExtensionUninstallDialog::ExtensionUninstallDialog( ...@@ -73,6 +73,7 @@ ExtensionUninstallDialog::ExtensionUninstallDialog(
gfx::NativeWindow parent, gfx::NativeWindow parent,
ExtensionUninstallDialog::Delegate* delegate) ExtensionUninstallDialog::Delegate* delegate)
: profile_(profile), parent_(parent), delegate_(delegate) { : profile_(profile), parent_(parent), delegate_(delegate) {
DCHECK(delegate_);
if (parent) if (parent)
parent_window_tracker_ = NativeWindowTracker::Create(parent); parent_window_tracker_ = NativeWindowTracker::Create(parent);
} }
......
...@@ -4,8 +4,6 @@ ...@@ -4,8 +4,6 @@
#include <memory> #include <memory>
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
...@@ -25,11 +23,13 @@ ...@@ -25,11 +23,13 @@
#include "extensions/common/constants.h" #include "extensions/common/constants.h"
#include "extensions/common/extension.h" #include "extensions/common/extension.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
#include "ui/base/models/dialog_model.h"
#include "ui/compositor/compositor.h" #include "ui/compositor/compositor.h"
#include "ui/compositor/layer.h" #include "ui/compositor/layer.h"
#include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/insets.h"
#include "ui/gfx/image/image_skia_operations.h" #include "ui/gfx/image/image_skia_operations.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h" #include "ui/views/bubble/bubble_dialog_delegate_view.h"
#include "ui/views/bubble/bubble_dialog_model_host.h"
#include "ui/views/controls/button/checkbox.h" #include "ui/views/controls/button/checkbox.h"
#include "ui/views/controls/image_view.h" #include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h" #include "ui/views/controls/label.h"
...@@ -39,7 +39,8 @@ ...@@ -39,7 +39,8 @@
#include "ui/views/window/dialog_delegate.h" #include "ui/views/window/dialog_delegate.h"
namespace { namespace {
class ExtensionUninstallDialogDelegateView;
constexpr int kCheckboxId = 1;
// Views implementation of the uninstall dialog. // Views implementation of the uninstall dialog.
class ExtensionUninstallDialogViews class ExtensionUninstallDialogViews
...@@ -49,61 +50,28 @@ class ExtensionUninstallDialogViews ...@@ -49,61 +50,28 @@ class ExtensionUninstallDialogViews
Profile* profile, Profile* profile,
gfx::NativeWindow parent, gfx::NativeWindow parent,
extensions::ExtensionUninstallDialog::Delegate* delegate); extensions::ExtensionUninstallDialog::Delegate* delegate);
ExtensionUninstallDialogViews(const ExtensionUninstallDialogViews&) = delete;
ExtensionUninstallDialogViews& operator=(
const ExtensionUninstallDialogViews&) = delete;
~ExtensionUninstallDialogViews() override; ~ExtensionUninstallDialogViews() override;
// Called when the ExtensionUninstallDialogDelegate has been destroyed to make // Forwards that the dialog has been accepted to the delegate.
// sure we invalidate pointers. This object will also be freed. void DialogAccepted();
void DialogDelegateDestroyed(); // Reports a canceled dialog to the delegate (unless accepted).
void DialogClosing();
// Forwards the accept and cancels to the delegate.
void DialogAccepted(bool checkbox_checked);
void DialogCanceled();
private: private:
void Show() override; void Show() override;
ExtensionUninstallDialogDelegateView* view_ = nullptr; // Pointer to the DialogModel for the dialog. This is cleared when the dialog
// is being closed and OnDialogClosed is reported. As such it prevents access
DISALLOW_COPY_AND_ASSIGN(ExtensionUninstallDialogViews); // to the dialog after it's been closed, as well as preventing multiple
}; // reports of OnDialogClosed.
ui::DialogModel* dialog_model_ = nullptr;
// The dialog's view, owned by the views framework.
class ExtensionUninstallDialogDelegateView
: public views::BubbleDialogDelegateView {
public:
// Constructor for view component of dialog. triggering_extension may be null
// if the uninstall dialog was manually triggered (from chrome://extensions).
ExtensionUninstallDialogDelegateView(
ExtensionUninstallDialogViews* dialog_view,
ToolbarActionView* anchor_view,
const extensions::Extension* extension,
const extensions::Extension* triggering_extension,
const gfx::ImageSkia* image);
~ExtensionUninstallDialogDelegateView() override;
// Called when the ExtensionUninstallDialog has been destroyed to make sure
// we invalidate pointers.
void DialogDestroyed() { dialog_ = nullptr; }
private:
// views::View:
const char* GetClassName() const override;
// views::DialogDelegateView:
gfx::Size CalculatePreferredSize() const override;
// views::WidgetDelegate:
ui::ModalType GetModalType() const override {
return is_bubble_ ? ui::MODAL_TYPE_NONE : ui::MODAL_TYPE_WINDOW;
}
ExtensionUninstallDialogViews* dialog_;
const bool is_bubble_;
views::Label* heading_;
views::Checkbox* checkbox_;
DISALLOW_COPY_AND_ASSIGN(ExtensionUninstallDialogDelegateView); // WeakPtrs because the associated dialog may outlive |this|, which is owned
// by the caller of extensions::ExtensionsUninstallDialog::Create().
base::WeakPtrFactory<ExtensionUninstallDialogViews> weak_ptr_factory_{this};
}; };
ExtensionUninstallDialogViews::ExtensionUninstallDialogViews( ExtensionUninstallDialogViews::ExtensionUninstallDialogViews(
...@@ -113,14 +81,52 @@ ExtensionUninstallDialogViews::ExtensionUninstallDialogViews( ...@@ -113,14 +81,52 @@ ExtensionUninstallDialogViews::ExtensionUninstallDialogViews(
: extensions::ExtensionUninstallDialog(profile, parent, delegate) {} : extensions::ExtensionUninstallDialog(profile, parent, delegate) {}
ExtensionUninstallDialogViews::~ExtensionUninstallDialogViews() { ExtensionUninstallDialogViews::~ExtensionUninstallDialogViews() {
// Close the widget (the views framework will delete view_). if (dialog_model_)
if (view_) { dialog_model_->host()->Close();
view_->DialogDestroyed(); DCHECK(!dialog_model_);
view_->GetWidget()->CloseNow();
}
} }
void ExtensionUninstallDialogViews::Show() { void ExtensionUninstallDialogViews::Show() {
// TODO(pbos): Consider separating dialog model from views code.
ui::DialogModel::Builder dialog_builder;
dialog_builder
.SetTitle(
l10n_util::GetStringFUTF16(IDS_EXTENSION_PROMPT_UNINSTALL_TITLE,
base::UTF8ToUTF16(extension()->name())))
.OverrideShowCloseButton(false)
.SetWindowClosingCallback(
base::BindOnce(&ExtensionUninstallDialogViews::DialogClosing,
weak_ptr_factory_.GetWeakPtr()))
.SetIcon(ui::ImageModel::FromImageSkia(
gfx::ImageSkiaOperations::CreateResizedImage(
icon(), skia::ImageOperations::ResizeMethod::RESIZE_GOOD,
gfx::Size(extension_misc::EXTENSION_ICON_SMALL,
extension_misc::EXTENSION_ICON_SMALL))))
.AddOkButton(
base::BindOnce(&ExtensionUninstallDialogViews::DialogAccepted,
weak_ptr_factory_.GetWeakPtr()),
l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_UNINSTALL_BUTTON))
.AddCancelButton(
base::OnceClosure() /* Cancel is covered by WindowClosingCallback */);
if (triggering_extension()) {
dialog_builder.AddBodyText(
ui::DialogModelLabel(
l10n_util::GetStringFUTF16(
IDS_EXTENSION_PROMPT_UNINSTALL_TRIGGERED_BY_EXTENSION,
base::UTF8ToUTF16(triggering_extension()->name())))
.set_is_secondary()
.set_allow_character_break());
}
if (ShouldShowCheckbox()) {
dialog_builder.AddCheckbox(kCheckboxId,
ui::DialogModelLabel(GetCheckboxLabel()));
}
std::unique_ptr<ui::DialogModel> dialog_model = dialog_builder.Build();
dialog_model_ = dialog_model.get();
BrowserView* const browser_view = BrowserView* const browser_view =
parent() ? BrowserView::GetBrowserViewForNativeWindow(parent()) : nullptr; parent() ? BrowserView::GetBrowserViewForNativeWindow(parent()) : nullptr;
ToolbarActionView* anchor_view = nullptr; ToolbarActionView* anchor_view = nullptr;
...@@ -141,154 +147,48 @@ void ExtensionUninstallDialogViews::Show() { ...@@ -141,154 +147,48 @@ void ExtensionUninstallDialogViews::Show() {
if (reference_view && reference_view->GetVisible()) if (reference_view && reference_view->GetVisible())
anchor_view = reference_view; anchor_view = reference_view;
} }
view_ = new ExtensionUninstallDialogDelegateView(
this, anchor_view, extension(), triggering_extension(), &icon());
if (anchor_view) { if (anchor_view) {
auto bubble = std::make_unique<views::BubbleDialogModelHost>(
std::move(dialog_model), anchor_view, views::BubbleBorder::TOP_RIGHT);
if (container) { if (container) {
container->ShowWidgetForExtension( container->ShowWidgetForExtension(
views::BubbleDialogDelegateView::CreateBubble(view_), views::BubbleDialogDelegateView::CreateBubble(std::move(bubble)),
extension()->id()); extension()->id());
} else { } else {
DCHECK(!base::FeatureList::IsEnabled(features::kExtensionsToolbarMenu)); DCHECK(!base::FeatureList::IsEnabled(features::kExtensionsToolbarMenu));
views::BubbleDialogDelegateView::CreateBubble(view_)->Show(); views::BubbleDialogDelegateView::CreateBubble(std::move(bubble))->Show();
} }
} else { } else {
constrained_window::CreateBrowserModalDialogViews(view_, parent())->Show(); // TODO(pbos): Add unique_ptr version of CreateBrowserModalDialogViews and
} // remove .release().
} constrained_window::CreateBrowserModalDialogViews(
views::BubbleDialogModelHost::CreateModal(std::move(dialog_model),
void ExtensionUninstallDialogViews::DialogDelegateDestroyed() { ui::MODAL_TYPE_WINDOW)
// Checks view_ to ensure OnDialogClosed() will not be called twice. .release(),
if (view_) { parent())
view_ = nullptr; ->Show();
OnDialogClosed(CLOSE_ACTION_CANCELED);
}
}
void ExtensionUninstallDialogViews::DialogAccepted(bool checkbox_checked) {
// The widget gets destroyed when the dialog is accepted.
DCHECK(view_);
view_->DialogDestroyed();
view_ = nullptr;
OnDialogClosed(checkbox_checked ? CLOSE_ACTION_UNINSTALL_AND_CHECKBOX_CHECKED
: CLOSE_ACTION_UNINSTALL);
}
void ExtensionUninstallDialogViews::DialogCanceled() {
// The widget gets destroyed when the dialog is canceled.
DCHECK(view_);
view_->DialogDestroyed();
view_ = nullptr;
OnDialogClosed(CLOSE_ACTION_CANCELED);
}
ExtensionUninstallDialogDelegateView::ExtensionUninstallDialogDelegateView(
ExtensionUninstallDialogViews* dialog_view,
ToolbarActionView* anchor_view,
const extensions::Extension* extension,
const extensions::Extension* triggering_extension,
const gfx::ImageSkia* image)
: BubbleDialogDelegateView(anchor_view,
anchor_view ? views::BubbleBorder::TOP_RIGHT
: views::BubbleBorder::NONE),
dialog_(dialog_view),
is_bubble_(anchor_view != nullptr),
checkbox_(nullptr) {
SetButtonLabel(
ui::DIALOG_BUTTON_OK,
l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_UNINSTALL_BUTTON));
SetIcon(gfx::ImageSkiaOperations::CreateResizedImage(
*image, skia::ImageOperations::ResizeMethod::RESIZE_GOOD,
gfx::Size(extension_misc::EXTENSION_ICON_SMALL,
extension_misc::EXTENSION_ICON_SMALL)));
SetShowCloseButton(false);
SetShowIcon(true);
SetTitle(l10n_util::GetStringFUTF16(IDS_EXTENSION_PROMPT_UNINSTALL_TITLE,
base::UTF8ToUTF16(extension->name())));
SetAcceptCallback(base::BindOnce(
[](ExtensionUninstallDialogDelegateView* view) {
if (view->dialog_) {
view->dialog_->DialogAccepted(view->checkbox_ &&
view->checkbox_->GetChecked());
}
},
base::Unretained(this)));
SetCancelCallback(base::BindOnce(
[](ExtensionUninstallDialogDelegateView* view) {
if (view->dialog_)
view->dialog_->DialogCanceled();
},
base::Unretained(this)));
ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical, gfx::Insets(),
provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL)));
// Add margins for the icon plus the icon-title padding so that the dialog
// contents align with the title text.
set_margins(
margins() +
gfx::Insets(0, margins().left() + extension_misc::EXTENSION_ICON_SMALL, 0,
0));
if (triggering_extension) {
heading_ = new views::Label(
l10n_util::GetStringFUTF16(
IDS_EXTENSION_PROMPT_UNINSTALL_TRIGGERED_BY_EXTENSION,
base::UTF8ToUTF16(triggering_extension->name())),
views::style::CONTEXT_DIALOG_BODY_TEXT, views::style::STYLE_SECONDARY);
heading_->SetMultiLine(true);
heading_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
heading_->SetAllowCharacterBreak(true);
AddChildView(heading_);
}
if (dialog_->ShouldShowCheckbox()) {
checkbox_ = new views::Checkbox(dialog_->GetCheckboxLabel());
checkbox_->SetMultiLine(true);
AddChildView(checkbox_);
} }
if (anchor_view)
anchor_view->AnimateInkDrop(views::InkDropState::ACTIVATED, nullptr);
chrome::RecordDialogCreation(chrome::DialogIdentifier::EXTENSION_UNINSTALL); chrome::RecordDialogCreation(chrome::DialogIdentifier::EXTENSION_UNINSTALL);
} }
ExtensionUninstallDialogDelegateView::~ExtensionUninstallDialogDelegateView() { void ExtensionUninstallDialogViews::DialogAccepted() {
// If we're here, 2 things could have happened. Either the user closed the DCHECK(dialog_model_);
// dialog nicely and one of the installed/canceled methods has been called const bool checkbox_is_checked =
// (in which case dialog_ will be null), *or* neither of them have been ShouldShowCheckbox() &&
// called and we are being forced closed by our parent widget. In this case, dialog_model_->GetCheckboxByUniqueId(kCheckboxId)->is_checked();
// we need to make sure to notify dialog_ not to call us again, since we're dialog_model_ = nullptr;
// about to be freed by the Widget framework. OnDialogClosed(checkbox_is_checked
if (dialog_) ? CLOSE_ACTION_UNINSTALL_AND_CHECKBOX_CHECKED
dialog_->DialogDelegateDestroyed(); : CLOSE_ACTION_UNINSTALL);
// If there is still a toolbar action view its ink drop should be deactivated
// when the uninstall dialog goes away. This lookup is repeated as the dialog
// can go away during dialog's lifetime (especially when uninstalling).
views::View* anchor_view = GetAnchorView();
if (anchor_view) {
static_cast<ToolbarActionView*>(anchor_view)
->AnimateInkDrop(views::InkDropState::DEACTIVATED, nullptr);
}
} }
const char* ExtensionUninstallDialogDelegateView::GetClassName() const { void ExtensionUninstallDialogViews::DialogClosing() {
return "ExtensionUninstallDialogDelegateView"; if (!dialog_model_)
} return;
dialog_model_ = nullptr;
gfx::Size ExtensionUninstallDialogDelegateView::CalculatePreferredSize() const { OnDialogClosed(CLOSE_ACTION_CANCELED);
const int width =
ChromeLayoutProvider::Get()->GetDistanceMetric(
is_bubble_ ? views::DISTANCE_BUBBLE_PREFERRED_WIDTH
: views::DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH) -
margins().width();
return gfx::Size(width, GetHeightForWidth(width));
} }
} // namespace } // namespace
......
...@@ -135,7 +135,6 @@ IN_PROC_BROWSER_TEST_F(ExtensionUninstallDialogViewBrowserTest, ...@@ -135,7 +135,6 @@ IN_PROC_BROWSER_TEST_F(ExtensionUninstallDialogViewBrowserTest,
EXPECT_TRUE(delegate.canceled()); EXPECT_TRUE(delegate.canceled());
} }
#if defined(OS_CHROMEOS)
// Test that we don't crash when uninstalling an extension from a web app // Test that we don't crash when uninstalling an extension from a web app
// window in Ash. Context: crbug.com/825554 // window in Ash. Context: crbug.com/825554
IN_PROC_BROWSER_TEST_F(ExtensionUninstallDialogViewBrowserTest, IN_PROC_BROWSER_TEST_F(ExtensionUninstallDialogViewBrowserTest,
...@@ -155,12 +154,13 @@ IN_PROC_BROWSER_TEST_F(ExtensionUninstallDialogViewBrowserTest, ...@@ -155,12 +154,13 @@ IN_PROC_BROWSER_TEST_F(ExtensionUninstallDialogViewBrowserTest,
Browser* app_browser = Browser* app_browser =
web_app::LaunchWebAppBrowser(browser()->profile(), app_id); web_app::LaunchWebAppBrowser(browser()->profile(), app_id);
TestExtensionUninstallDialogDelegate delegate{base::DoNothing()};
std::unique_ptr<extensions::ExtensionUninstallDialog> dialog; std::unique_ptr<extensions::ExtensionUninstallDialog> dialog;
{ {
base::RunLoop run_loop; base::RunLoop run_loop;
dialog = extensions::ExtensionUninstallDialog::Create( dialog = extensions::ExtensionUninstallDialog::Create(
app_browser->profile(), app_browser->window()->GetNativeWindow(), app_browser->profile(), app_browser->window()->GetNativeWindow(),
nullptr); &delegate);
run_loop.RunUntilIdle(); run_loop.RunUntilIdle();
} }
...@@ -172,7 +172,6 @@ IN_PROC_BROWSER_TEST_F(ExtensionUninstallDialogViewBrowserTest, ...@@ -172,7 +172,6 @@ IN_PROC_BROWSER_TEST_F(ExtensionUninstallDialogViewBrowserTest,
run_loop.RunUntilIdle(); run_loop.RunUntilIdle();
} }
} }
#endif // defined(OS_CHROMEOS)
class ParameterizedExtensionUninstallDialogViewBrowserTest class ParameterizedExtensionUninstallDialogViewBrowserTest
: public InProcessBrowserTest, : public InProcessBrowserTest,
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include "extensions/test/test_extension_dir.h" #include "extensions/test/test_extension_dir.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "ui/views/animation/ink_drop.h" #include "ui/views/animation/ink_drop.h"
#include "ui/views/bubble/bubble_dialog_model_host.h"
#include "ui/views/controls/button/image_button.h" #include "ui/views/controls/button/image_button.h"
#include "ui/views/layout/animating_layout_manager.h" #include "ui/views/layout/animating_layout_manager.h"
#include "ui/views/layout/animating_layout_manager_test_util.h" #include "ui/views/layout/animating_layout_manager_test_util.h"
...@@ -88,7 +89,7 @@ class ExtensionsMenuViewBrowserTest : public ExtensionsToolbarBrowserTest { ...@@ -88,7 +89,7 @@ class ExtensionsMenuViewBrowserTest : public ExtensionsToolbarBrowserTest {
// Trigger uninstall dialog. // Trigger uninstall dialog.
views::NamedWidgetShownWaiter waiter( views::NamedWidgetShownWaiter waiter(
views::test::AnyWidgetTestPasskey{}, views::test::AnyWidgetTestPasskey{},
"ExtensionUninstallDialogDelegateView"); views::BubbleDialogModelHost::kViewClassName);
extensions::ExtensionContextMenuModel menu_model( extensions::ExtensionContextMenuModel menu_model(
extensions()[0].get(), browser(), extensions()[0].get(), browser(),
extensions::ExtensionContextMenuModel::PINNED, nullptr, extensions::ExtensionContextMenuModel::PINNED, nullptr,
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "base/util/type_safety/pass_key.h" #include "base/util/type_safety/pass_key.h"
#include "ui/base/models/dialog_model_field.h" #include "ui/base/models/dialog_model_field.h"
#include "ui/base/models/dialog_model_host.h" #include "ui/base/models/dialog_model_host.h"
#include "ui/base/models/image_model.h"
#include "ui/base/ui_base_types.h" #include "ui/base/ui_base_types.h"
namespace ui { namespace ui {
...@@ -126,6 +127,11 @@ class COMPONENT_EXPORT(UI_BASE) DialogModel final { ...@@ -126,6 +127,11 @@ class COMPONENT_EXPORT(UI_BASE) DialogModel final {
return *this; return *this;
} }
Builder& SetIcon(ImageModel icon) {
model_->icon_ = std::move(icon);
return *this;
}
// Make screen readers announce the contents of the dialog as it appears. // Make screen readers announce the contents of the dialog as it appears.
// See |ax::mojom::Role::kAlertDialog|. // See |ax::mojom::Role::kAlertDialog|.
Builder& SetIsAlertDialog() { Builder& SetIsAlertDialog() {
...@@ -281,6 +287,8 @@ class COMPONENT_EXPORT(UI_BASE) DialogModel final { ...@@ -281,6 +287,8 @@ class COMPONENT_EXPORT(UI_BASE) DialogModel final {
return title_; return title_;
} }
const ImageModel& icon(util::PassKey<DialogModelHost>) const { return icon_; }
base::Optional<int> initially_focused_field( base::Optional<int> initially_focused_field(
util::PassKey<DialogModelHost>) const { util::PassKey<DialogModelHost>) const {
return initially_focused_field_; return initially_focused_field_;
...@@ -327,8 +335,8 @@ class COMPONENT_EXPORT(UI_BASE) DialogModel final { ...@@ -327,8 +335,8 @@ class COMPONENT_EXPORT(UI_BASE) DialogModel final {
base::Optional<bool> override_show_close_button_; base::Optional<bool> override_show_close_button_;
bool close_on_deactivate_ = true; bool close_on_deactivate_ = true;
base::string16 title_; base::string16 title_;
ImageModel icon_;
static constexpr int kExtraButtonId = DIALOG_BUTTON_LAST + 1;
std::vector<std::unique_ptr<DialogModelField>> fields_; std::vector<std::unique_ptr<DialogModelField>> fields_;
base::Optional<int> initially_focused_field_; base::Optional<int> initially_focused_field_;
bool is_alert_dialog_ = false; bool is_alert_dialog_ = false;
......
...@@ -3,7 +3,9 @@ ...@@ -3,7 +3,9 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "ui/base/models/dialog_model_field.h" #include "ui/base/models/dialog_model_field.h"
#include "base/bind.h" #include "base/bind.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/models/dialog_model.h" #include "ui/base/models/dialog_model.h"
namespace ui { namespace ui {
...@@ -18,9 +20,23 @@ DialogModelLabel::Link::Link(int message_id, base::RepeatingClosure closure) ...@@ -18,9 +20,23 @@ DialogModelLabel::Link::Link(int message_id, base::RepeatingClosure closure)
DialogModelLabel::Link::Link(const Link&) = default; DialogModelLabel::Link::Link(const Link&) = default;
DialogModelLabel::Link::~Link() = default; DialogModelLabel::Link::~Link() = default;
DialogModelLabel::DialogModelLabel(int message_id) : message_id_(message_id) {} DialogModelLabel::DialogModelLabel(int message_id)
: message_id_(message_id),
string_(l10n_util::GetStringUTF16(message_id_)) {}
DialogModelLabel::DialogModelLabel(int message_id, std::vector<Link> links) DialogModelLabel::DialogModelLabel(int message_id, std::vector<Link> links)
: message_id_(message_id), links_(std::move(links)) {} : message_id_(message_id), links_(std::move(links)) {
// Note that this constructor does not set |string_| which is invalid for
// labels with links.
}
DialogModelLabel::DialogModelLabel(base::string16 fixed_string)
: message_id_(-1), string_(std::move(fixed_string)) {}
const base::string16& DialogModelLabel::GetString(
util::PassKey<DialogModelHost>) const {
DCHECK(links_.empty());
return string_;
}
DialogModelLabel::DialogModelLabel(const DialogModelLabel&) = default; DialogModelLabel::DialogModelLabel(const DialogModelLabel&) = default;
......
...@@ -45,6 +45,7 @@ class COMPONENT_EXPORT(UI_BASE) DialogModelLabel { ...@@ -45,6 +45,7 @@ class COMPONENT_EXPORT(UI_BASE) DialogModelLabel {
}; };
explicit DialogModelLabel(int message_id); explicit DialogModelLabel(int message_id);
explicit DialogModelLabel(base::string16 fixed_string);
DialogModelLabel(const DialogModelLabel&); DialogModelLabel(const DialogModelLabel&);
DialogModelLabel& operator=(const DialogModelLabel&) = delete; DialogModelLabel& operator=(const DialogModelLabel&) = delete;
~DialogModelLabel(); ~DialogModelLabel();
...@@ -54,11 +55,22 @@ class COMPONENT_EXPORT(UI_BASE) DialogModelLabel { ...@@ -54,11 +55,22 @@ class COMPONENT_EXPORT(UI_BASE) DialogModelLabel {
static DialogModelLabel CreateWithLinks(int message_id, static DialogModelLabel CreateWithLinks(int message_id,
std::vector<Link> links); std::vector<Link> links);
// Gets the string. Not for use with links, in which case the caller must use
// links() and message_id() to construct the final label. This is required to
// style the final label appropriately and support link callbacks. The caller
// is responsible for checking links().empty() before calling this.
const base::string16& GetString(util::PassKey<DialogModelHost>) const;
DialogModelLabel& set_is_secondary() { DialogModelLabel& set_is_secondary() {
is_secondary_ = true; is_secondary_ = true;
return *this; return *this;
} }
DialogModelLabel& set_allow_character_break() {
allow_character_break_ = true;
return *this;
}
int message_id(util::PassKey<DialogModelHost>) const { return message_id_; } int message_id(util::PassKey<DialogModelHost>) const { return message_id_; }
const std::vector<Link> links(util::PassKey<DialogModelHost>) const { const std::vector<Link> links(util::PassKey<DialogModelHost>) const {
return links_; return links_;
...@@ -66,13 +78,18 @@ class COMPONENT_EXPORT(UI_BASE) DialogModelLabel { ...@@ -66,13 +78,18 @@ class COMPONENT_EXPORT(UI_BASE) DialogModelLabel {
bool is_secondary(util::PassKey<DialogModelHost>) const { bool is_secondary(util::PassKey<DialogModelHost>) const {
return is_secondary_; return is_secondary_;
} }
bool allow_character_break(util::PassKey<DialogModelHost>) const {
return allow_character_break_;
}
private: private:
explicit DialogModelLabel(int message_id, std::vector<Link> links); explicit DialogModelLabel(int message_id, std::vector<Link> links);
const int message_id_; const int message_id_;
const base::string16 string_;
const std::vector<Link> links_; const std::vector<Link> links_;
bool is_secondary_ = false; bool is_secondary_ = false;
bool allow_character_break_ = false;
}; };
// These "field" classes represent entries in a DialogModel. They are owned // These "field" classes represent entries in a DialogModel. They are owned
......
...@@ -134,11 +134,19 @@ BubbleDialogModelHost::BubbleDialogModelHost( ...@@ -134,11 +134,19 @@ BubbleDialogModelHost::BubbleDialogModelHost(
SetButtons(button_mask); SetButtons(button_mask);
SetTitle(model_->title(GetPassKey())); SetTitle(model_->title(GetPassKey()));
if (model_->override_show_close_button(GetPassKey())) { if (model_->override_show_close_button(GetPassKey())) {
SetShowCloseButton(*model_->override_show_close_button(GetPassKey())); SetShowCloseButton(*model_->override_show_close_button(GetPassKey()));
} else { } else {
SetShowCloseButton(!IsModalDialog()); SetShowCloseButton(!IsModalDialog());
} }
if (!model_->icon(GetPassKey()).IsEmpty()) {
// TODO(pbos): Consider adding ImageModel support to SetIcon().
SetIcon(model_->icon(GetPassKey()).GetImage().AsImageSkia());
SetShowIcon(true);
}
if (model_->is_alert_dialog(GetPassKey())) if (model_->is_alert_dialog(GetPassKey()))
SetAccessibleRole(ax::mojom::Role::kAlertDialog); SetAccessibleRole(ax::mojom::Role::kAlertDialog);
...@@ -290,8 +298,20 @@ void BubbleDialogModelHost::AddInitialFields() { ...@@ -290,8 +298,20 @@ void BubbleDialogModelHost::AddInitialFields() {
first_row = false; first_row = false;
} }
set_margins(LayoutProvider::Get()->GetDialogInsetsForContentType( gfx::Insets margins = LayoutProvider::Get()->GetDialogInsetsForContentType(
first_field_content_type, last_field_content_type)); first_field_content_type, last_field_content_type);
if (!model_->icon(GetPassKey()).IsEmpty()) {
// If we have a window icon, inset margins additionally to align with
// title label.
// TODO(pbos): Reconsider this. Aligning with title gives a massive gap on
// the left side of the dialog. This style is from
// ExtensionUninstallDialogView as part of refactoring it to use
// DialogModel.
margins.set_left(
margins.left() + model_->icon(GetPassKey()).Size().width() +
LayoutProvider::Get()->GetInsetsMetric(INSETS_DIALOG_TITLE).left());
}
set_margins(margins);
} }
void BubbleDialogModelHost::OnWindowClosing() { void BubbleDialogModelHost::OnWindowClosing() {
...@@ -479,8 +499,7 @@ std::unique_ptr<View> BubbleDialogModelHost::CreateViewForLabel( ...@@ -479,8 +499,7 @@ std::unique_ptr<View> BubbleDialogModelHost::CreateViewForLabel(
} }
auto text_label = std::make_unique<Label>( auto text_label = std::make_unique<Label>(
l10n_util::GetStringUTF16(dialog_label.message_id(GetPassKey())), dialog_label.GetString(GetPassKey()), style::CONTEXT_DIALOG_BODY_TEXT,
style::CONTEXT_DIALOG_BODY_TEXT,
dialog_label.is_secondary(GetPassKey()) ? style::STYLE_SECONDARY dialog_label.is_secondary(GetPassKey()) ? style::STYLE_SECONDARY
: style::STYLE_PRIMARY); : style::STYLE_PRIMARY);
text_label->SetMultiLine(true); text_label->SetMultiLine(true);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment