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

Add a model-based dialog framework

Introduces a formal split between a dialog model and the bubble in which
it's hosted. Refactors BookmarkBubbleView to use it.

Several DCHECKs are in place and TODOs that need to be resolved while
converting more dialogs to make this more flexible.

Bug: 1106422
Change-Id: Ied4baeaf3237f22f770562dc3161030e8640354d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2292700
Commit-Queue: Peter Boström <pbos@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarElly Fong-Jones <ellyjones@chromium.org>
Cr-Commit-Position: refs/heads/master@{#797105}
parent d3395285
......@@ -8,15 +8,9 @@
#include <memory>
#include "base/macros.h"
#include "base/strings/string16.h"
#include "chrome/browser/ui/bookmarks/recently_used_folders_combo_model.h"
#include "chrome/browser/ui/sync/bubble_sync_promo_delegate.h"
#include "chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/combobox/combobox.h"
#include "ui/views/controls/combobox/combobox_listener.h"
#include "url/gurl.h"
class GURL;
class Profile;
namespace bookmarks {
......@@ -24,116 +18,33 @@ class BookmarkBubbleObserver;
}
namespace views {
class Textfield;
class BubbleDialogDelegate;
class Button;
class View;
}
// BookmarkBubbleView is a view intended to be used as the content of an
// Bubble. BookmarkBubbleView provides views for unstarring and editing the
// bookmark it is created with. Don't create a BookmarkBubbleView directly,
// instead use the static Show method.
class BookmarkBubbleView : public LocationBarBubbleDelegateView,
public views::ButtonListener,
public views::ComboboxListener {
// BookmarkBubbleView provides a dialog for unstarring and editing the bookmark
// it is created with. The dialog is created using the static ShowBubble method.
class BookmarkBubbleView {
public:
METADATA_HEADER(BookmarkBubbleView);
// If |anchor_view| is null, |anchor_rect| is used to anchor the bubble and
// |parent_window| is used to ensure the bubble closes if the parent closes.
// Returns the newly created bubble's Widget or nullptr in case when the
// bubble already exists.
static views::Widget* ShowBubble(
views::View* anchor_view,
views::Button* highlighted_button,
bookmarks::BookmarkBubbleObserver* observer,
std::unique_ptr<BubbleSyncPromoDelegate> delegate,
Profile* profile,
const GURL& url,
bool already_bookmarked);
static void ShowBubble(views::View* anchor_view,
views::Button* highlighted_button,
bookmarks::BookmarkBubbleObserver* observer,
std::unique_ptr<BubbleSyncPromoDelegate> delegate,
Profile* profile,
const GURL& url,
bool already_bookmarked);
static void Hide();
static BookmarkBubbleView* bookmark_bubble() { return bookmark_bubble_; }
~BookmarkBubbleView() override;
// LocationBarBubbleDelegateView:
View* GetInitiallyFocusedView() override;
void WindowClosing() override;
void OnDialogInitialized() override;
// views::ButtonListener:
void ButtonPressed(views::Button* sender, const ui::Event& event) override;
// views::ComboboxListener:
void OnPerformAction(views::Combobox* combobox) override;
protected:
// LocationBarBubbleDelegateView:
void Init() override;
private:
friend class BookmarkBubbleViewTest;
friend class BookmarkBubbleViewBrowserTest;
// Creates a BookmarkBubbleView.
BookmarkBubbleView(views::View* anchor_view,
bookmarks::BookmarkBubbleObserver* observer,
std::unique_ptr<BubbleSyncPromoDelegate> delegate,
Profile* profile,
const GURL& url,
bool newly_bookmarked);
// Returns the name of the bookmark.
base::string16 GetBookmarkName();
// Returns the model used to initialize |parent_combobox_|.
RecentlyUsedFoldersComboModel* folder_model() {
return static_cast<RecentlyUsedFoldersComboModel*>(
parent_combobox_->model());
static views::BubbleDialogDelegate* bookmark_bubble() {
return bookmark_bubble_;
}
// Shows the BookmarkEditor.
void ShowEditor();
// Sets the bookmark name and parent of the node.
void ApplyEdits();
// Creates the signin promo view, if there should be one.
std::unique_ptr<views::View> CreateSigninPromoView();
void OnDialogCancelled();
private:
class BookmarkBubbleDelegate;
// The bookmark bubble, if we're showing one.
static BookmarkBubbleView* bookmark_bubble_;
// Our observer, to notify when the bubble shows or hides.
bookmarks::BookmarkBubbleObserver* observer_;
// Delegate, to handle clicks on the sign in link.
std::unique_ptr<BubbleSyncPromoDelegate> delegate_;
// The profile.
Profile* profile_;
// The bookmark URL.
const GURL url_;
// Textfield showing the name of the bookmark.
views::Textfield* name_field_ = nullptr;
// Combobox showing a handful of folders the user can choose from, including
// the current parent.
views::Combobox* parent_combobox_ = nullptr;
// The regular bookmark bubble contents, with all the edit fields and dialog
// buttons. TODO(tapted): Move the buttons to the DialogClientView.
views::View* bookmark_contents_view_ = nullptr;
// When the destructor is invoked should the bookmark be removed?
bool remove_bookmark_ = false;
// When the destructor is invoked should edits be applied?
bool apply_edits_ = true;
static views::BubbleDialogDelegate* bookmark_bubble_;
DISALLOW_COPY_AND_ASSIGN(BookmarkBubbleView);
};
......
......@@ -18,6 +18,7 @@
#include "components/bookmarks/test/bookmark_test_helpers.h"
#include "components/signin/public/identity_manager/identity_test_utils.h"
#include "content/public/test/browser_test.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
class BookmarkBubbleViewBrowserTest : public DialogBrowserTest {
public:
......
......@@ -15,9 +15,13 @@
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/ui/sync/bubble_sync_promo_delegate.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/views/chrome_test_widget.h"
#include "components/bookmarks/browser/bookmark_utils.h"
#include "components/bookmarks/test/bookmark_test_helpers.h"
#include "components/signin/public/identity_manager/identity_test_utils.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
#include "ui/views/test/widget_test.h"
#include "ui/views/widget/unique_widget_ptr.h"
using bookmarks::BookmarkModel;
......@@ -39,6 +43,12 @@ class BookmarkBubbleViewTest : public BrowserWithTestWindowTest {
void SetUp() override {
BrowserWithTestWindowTest::SetUp();
anchor_widget_ =
views::UniqueWidgetPtr(std::make_unique<ChromeTestWidget>());
views::Widget::InitParams widget_params;
widget_params.context = GetContext();
anchor_widget_->Init(std::move(widget_params));
BookmarkModel* bookmark_model =
BookmarkModelFactory::GetForBrowserContext(profile());
bookmarks::test::WaitForBookmarkModelToLoad(bookmark_model);
......@@ -49,7 +59,12 @@ class BookmarkBubbleViewTest : public BrowserWithTestWindowTest {
void TearDown() override {
// Make sure the bubble is destroyed before the profile to avoid a crash.
bubble_.reset();
views::test::WidgetDestroyedWaiter destroyed_waiter(
BookmarkBubbleView::bookmark_bubble()->GetWidget());
BookmarkBubbleView::bookmark_bubble()->GetWidget()->Close();
destroyed_waiter.Wait();
anchor_widget_.reset();
BrowserWithTestWindowTest::TearDown();
}
......@@ -63,18 +78,13 @@ class BookmarkBubbleViewTest : public BrowserWithTestWindowTest {
// Creates a bookmark bubble view.
void CreateBubbleView() {
// Create a fake anchor view for the bubble.
anchor_ = std::make_unique<views::View>();
bubble_.reset(new BookmarkBubbleView(anchor_.get(), nullptr, nullptr,
profile(), GURL(kTestBookmarkURL),
true));
bubble_->Init();
BookmarkBubbleView::ShowBubble(anchor_widget_->GetContentsView(), nullptr,
nullptr, nullptr, profile(),
GURL(kTestBookmarkURL), true);
}
std::unique_ptr<BookmarkBubbleView> bubble_;
private:
std::unique_ptr<views::View> anchor_;
views::UniqueWidgetPtr anchor_widget_;
DISALLOW_COPY_AND_ASSIGN(BookmarkBubbleViewTest);
};
......@@ -85,13 +95,15 @@ TEST_F(BookmarkBubbleViewTest, SyncPromoSignedIn) {
IdentityManagerFactory::GetForProfile(profile()),
"fake_username@gmail.com");
CreateBubbleView();
EXPECT_FALSE(bubble_->GetFootnoteViewForTesting());
EXPECT_FALSE(
BookmarkBubbleView::bookmark_bubble()->GetFootnoteViewForTesting());
}
// Verifies that the sync promo is displayed for a user that is not signed in.
TEST_F(BookmarkBubbleViewTest, SyncPromoNotSignedIn) {
CreateBubbleView();
views::View* footnote = bubble_->GetFootnoteViewForTesting();
views::View* footnote =
BookmarkBubbleView::bookmark_bubble()->GetFootnoteViewForTesting();
#if defined(OS_CHROMEOS)
EXPECT_FALSE(footnote);
#else // !defined(OS_CHROMEOS)
......
......@@ -118,6 +118,11 @@ component("base") {
"models/combobox_model.cc",
"models/combobox_model.h",
"models/combobox_model_observer.h",
"models/dialog_model.cc",
"models/dialog_model.h",
"models/dialog_model_field.cc",
"models/dialog_model_field.h",
"models/dialog_model_host.h",
"models/image_model.cc",
"models/image_model.h",
"models/list_model.h",
......@@ -436,6 +441,7 @@ component("base") {
"//base:base_static",
"//base:i18n",
"//base/third_party/dynamic_annotations",
"//base/util/type_safety:type_safety",
"//net",
"//third_party/brotli:dec",
"//third_party/icu",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/base/models/dialog_model.h"
namespace ui {
DialogModel::Builder::Builder(std::unique_ptr<DialogModelDelegate> delegate)
: model_(std::make_unique<DialogModel>(util::PassKey<Builder>(),
std::move(delegate))) {}
DialogModel::Builder::~Builder() {
DCHECK(!model_) << "Model should've been built.";
}
std::unique_ptr<DialogModel> DialogModel::Builder::Build() {
DCHECK(model_);
return std::move(model_);
}
DialogModel::Builder& DialogModel::Builder::SetShowCloseButton(
bool show_close_button) {
model_->show_close_button_ = show_close_button;
return *this;
}
DialogModel::Builder& DialogModel::Builder::SetTitle(base::string16 title) {
model_->title_ = std::move(title);
return *this;
}
DialogModel::Builder& DialogModel::Builder::SetAcceptCallback(
base::OnceClosure callback) {
model_->accept_callback_ = std::move(callback);
return *this;
}
DialogModel::Builder& DialogModel::Builder::SetCancelCallback(
base::OnceClosure callback) {
model_->cancel_callback_ = std::move(callback);
return *this;
}
DialogModel::Builder& DialogModel::Builder::SetCloseCallback(
base::OnceClosure callback) {
model_->close_callback_ = std::move(callback);
return *this;
}
DialogModel::Builder& DialogModel::Builder::SetWindowClosingCallback(
base::OnceClosure callback) {
model_->window_closing_callback_ = std::move(callback);
return *this;
}
DialogModel::Builder& DialogModel::Builder::AddDialogButton(
DialogButton button,
base::string16 label,
const DialogModelButton::Params& params) {
model_->AddDialogButton(button, std::move(label), params);
return *this;
}
DialogModel::Builder& DialogModel::Builder::AddDialogExtraButton(
base::string16 label,
const DialogModelButton::Params& params) {
model_->AddDialogButton(kExtraButtonId, std::move(label), params);
return *this;
}
DialogModel::Builder& DialogModel::Builder::AddTextfield(
base::string16 label,
base::string16 text,
const DialogModelTextfield::Params& params) {
model_->AddTextfield(std::move(label), std::move(text), params);
return *this;
}
DialogModel::Builder& DialogModel::Builder::AddCombobox(
base::string16 label,
std::unique_ptr<ui::ComboboxModel> combobox_model,
const DialogModelCombobox::Params& params) {
model_->AddCombobox(std::move(label), std::move(combobox_model), params);
return *this;
}
DialogModel::Builder& DialogModel::Builder::SetInitiallyFocusedField(
int unique_id) {
// This must be called with unique_id >= 0 (-1 is "no ID").
DCHECK_GE(unique_id, 0);
// This can only be called once.
DCHECK(!model_->initially_focused_field_);
model_->initially_focused_field_ = unique_id;
return *this;
}
DialogModel::DialogModel(util::PassKey<Builder>,
std::unique_ptr<DialogModelDelegate> delegate)
: delegate_(std::move(delegate)) {
delegate_->set_dialog_model(this);
}
DialogModel::~DialogModel() = default;
void DialogModel::AddTextfield(base::string16 label,
base::string16 text,
const DialogModelTextfield::Params& params) {
fields_.push_back(std::make_unique<DialogModelTextfield>(
ReserveField(), std::move(label), std::move(text), params));
if (host_)
host_->OnModelChanged(this);
}
void DialogModel::AddCombobox(base::string16 label,
std::unique_ptr<ui::ComboboxModel> combobox_model,
const DialogModelCombobox::Params& params) {
fields_.push_back(std::make_unique<DialogModelCombobox>(
ReserveField(), std::move(label), std::move(combobox_model), params));
if (host_)
host_->OnModelChanged(this);
}
DialogModelField* DialogModel::GetFieldByUniqueId(int unique_id) {
for (auto& field : fields_) {
if (field->unique_id_ == unique_id)
return field.get();
}
NOTREACHED();
return nullptr;
}
DialogModelButton* DialogModel::GetButtonByUniqueId(int unique_id) {
auto* field = GetFieldByUniqueId(unique_id);
DCHECK_EQ(field->type_, DialogModelField::kButton);
return static_cast<DialogModelButton*>(field);
}
DialogModelCombobox* DialogModel::GetComboboxByUniqueId(int unique_id) {
auto* field = GetFieldByUniqueId(unique_id);
DCHECK_EQ(field->type_, DialogModelField::kCombobox);
return static_cast<DialogModelCombobox*>(field);
}
DialogModelTextfield* DialogModel::GetTextfieldByUniqueId(int unique_id) {
auto* field = GetFieldByUniqueId(unique_id);
DCHECK_EQ(field->type_, DialogModelField::kTextfield);
return static_cast<DialogModelTextfield*>(field);
}
DialogModelButton* DialogModel::GetDialogButton(DialogButton button) {
return GetButtonFromModelFieldId(button);
}
DialogModelButton* DialogModel::GetExtraButton() {
return GetButtonFromModelFieldId(kExtraButtonId);
}
void DialogModel::OnButtonPressed(util::PassKey<DialogModelHost>,
int id,
const Event& event) {
DCHECK_GT(id, DIALOG_BUTTON_LAST);
auto* button = GetButtonFromModelFieldId(id);
if (button->callback_)
button->callback_.Run(event);
}
void DialogModel::OnDialogAccepted(util::PassKey<DialogModelHost>) {
if (accept_callback_)
std::move(accept_callback_).Run();
}
void DialogModel::OnDialogCancelled(util::PassKey<DialogModelHost>) {
if (cancel_callback_)
std::move(cancel_callback_).Run();
}
void DialogModel::OnDialogClosed(util::PassKey<DialogModelHost>) {
if (close_callback_)
std::move(close_callback_).Run();
}
void DialogModel::OnComboboxSelectedIndexChanged(util::PassKey<DialogModelHost>,
int id,
int index) {
GetComboboxFromModelFieldId(id)->selected_index_ = index;
}
void DialogModel::OnComboboxPerformAction(util::PassKey<DialogModelHost>,
int id) {
auto* model = GetComboboxFromModelFieldId(id);
if (model->callback_)
model->callback_.Run();
}
void DialogModel::OnTextfieldTextChanged(util::PassKey<DialogModelHost>,
int id,
base::string16 text) {
GetTextfieldFromModelFieldId(id)->text_ = text;
}
void DialogModel::OnWindowClosing(util::PassKey<DialogModelHost>) {
if (window_closing_callback_)
std::move(window_closing_callback_).Run();
}
void DialogModel::AddDialogButton(int button,
base::string16 label,
const DialogModelButton::Params& params) {
DCHECK_LE(button, kExtraButtonId);
if (button != kExtraButtonId) // Dialog buttons should use dialog callbacks.
DCHECK(!params.has_callback());
DCHECK(!host_); // Dialog buttons should be added before adding to host.
DCHECK(!GetFieldFromModelFieldId(button));
fields_.push_back(std::make_unique<DialogModelButton>(
DialogModelField::Reservation(this, button), std::move(label), params));
}
DialogModelField* DialogModel::GetFieldFromModelFieldId(int id) {
for (const auto& field : fields_) {
if (id == field->model_field_id_)
return field.get();
}
return nullptr;
}
DialogModelButton* DialogModel::GetButtonFromModelFieldId(int id) {
auto* field = GetFieldFromModelFieldId(id);
DCHECK(field);
DCHECK_EQ(field->type_, DialogModelField::kButton);
return static_cast<DialogModelButton*>(field);
}
DialogModelCombobox* DialogModel::GetComboboxFromModelFieldId(int id) {
auto* field = GetFieldFromModelFieldId(id);
DCHECK(field);
DCHECK_EQ(field->type_, DialogModelField::kCombobox);
return static_cast<DialogModelCombobox*>(field);
}
DialogModelTextfield* DialogModel::GetTextfieldFromModelFieldId(int id) {
auto* field = GetFieldFromModelFieldId(id);
DCHECK(field);
DCHECK_EQ(field->type_, DialogModelField::kTextfield);
return static_cast<DialogModelTextfield*>(field);
}
DialogModelField::Reservation DialogModel::ReserveField() {
const int id = next_field_id_++;
DCHECK(!GetFieldFromModelFieldId(id));
return DialogModelField::Reservation(this, id);
}
} // namespace ui
\ No newline at end of file
This diff is collapsed.
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/base/models/dialog_model_field.h"
#include "ui/base/models/dialog_model.h"
namespace ui {
DialogModelField::DialogModelField(
const DialogModelField::Reservation& reservation,
DialogModelField::Type type,
int unique_id,
base::flat_set<Accelerator> accelerators)
: model_(reservation.model),
model_field_id_(reservation.model_field_id),
type_(type),
unique_id_(unique_id),
accelerators_(std::move(accelerators)) {
// TODO(pbos): Assert that unique_id_ is unique.
}
DialogModelField::~DialogModelField() = default;
DialogModelField::Reservation::Reservation(DialogModel* model,
int model_field_id)
: model(model), model_field_id(model_field_id) {}
DialogModelButton::Params::Params() = default;
DialogModelButton::Params::~Params() = default;
DialogModelButton::Params& DialogModelButton::Params::AddAccelerator(
Accelerator accelerator) {
accelerators_.insert(std::move(accelerator));
return *this;
}
DialogModelButton::Params& DialogModelButton::Params::SetCallback(
base::RepeatingCallback<void(const Event&)> callback) {
callback_ = std::move(callback);
return *this;
}
DialogModelButton::DialogModelButton(
const DialogModelField::Reservation& reservation,
base::string16 label,
const DialogModelButton::Params& params)
: DialogModelField(reservation,
kButton,
params.unique_id_,
params.accelerators_),
label_(std::move(label)),
callback_(params.callback_) {}
DialogModelButton::~DialogModelButton() = default;
DialogModelCombobox::Params::Params() = default;
DialogModelCombobox::Params::~Params() = default;
DialogModelCombobox::Params& DialogModelCombobox::Params::SetUniqueId(
int unique_id) {
DCHECK_GE(unique_id, 0);
unique_id_ = unique_id;
return *this;
}
DialogModelCombobox::Params& DialogModelCombobox::Params::SetCallback(
base::RepeatingClosure callback) {
callback_ = std::move(callback);
return *this;
}
DialogModelCombobox::Params& DialogModelCombobox::Params::AddAccelerator(
Accelerator accelerator) {
accelerators_.insert(std::move(accelerator));
return *this;
}
DialogModelCombobox::Params& DialogModelCombobox::Params::SetAccessibleName(
base::string16 accessible_name) {
accessible_name_ = std::move(accessible_name);
return *this;
}
DialogModelCombobox::DialogModelCombobox(
const DialogModelField::Reservation& reservation,
base::string16 label,
std::unique_ptr<ui::ComboboxModel> combobox_model,
const DialogModelCombobox::Params& params)
: DialogModelField(reservation,
kCombobox,
params.unique_id_,
params.accelerators_),
label_(std::move(label)),
accessible_name_(params.accessible_name_),
selected_index_(combobox_model->GetDefaultIndex()),
combobox_model_(std::move(combobox_model)),
callback_(params.callback_) {}
DialogModelCombobox::~DialogModelCombobox() = default;
DialogModelTextfield::Params::Params() = default;
DialogModelTextfield::Params::~Params() = default;
DialogModelTextfield::Params& DialogModelTextfield::Params::SetUniqueId(
int unique_id) {
DCHECK_GE(unique_id, 0);
unique_id_ = unique_id;
return *this;
}
DialogModelTextfield::Params& DialogModelTextfield::Params::AddAccelerator(
Accelerator accelerator) {
accelerators_.insert(std::move(accelerator));
return *this;
}
DialogModelTextfield::Params& DialogModelTextfield::Params::SetAccessibleName(
base::string16 accessible_name) {
accessible_name_ = accessible_name;
return *this;
}
DialogModelTextfield::DialogModelTextfield(
const DialogModelField::Reservation& reservation,
base::string16 label,
base::string16 text,
const ui::DialogModelTextfield::Params& params)
: DialogModelField(reservation,
kTextfield,
params.unique_id_,
params.accelerators_),
label_(label),
accessible_name_(params.accessible_name_),
text_(std::move(text)) {}
DialogModelTextfield::~DialogModelTextfield() = default;
} // namespace ui
\ No newline at end of file
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_BASE_MODELS_DIALOG_MODEL_FIELD_H_
#define UI_BASE_MODELS_DIALOG_MODEL_FIELD_H_
#include "base/callback.h"
#include "base/containers/flat_set.h"
#include "base/strings/string16.h"
#include "base/util/type_safety/pass_key.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/models/combobox_model.h"
namespace ui {
class DialogModel;
class DialogModelHost;
class Event;
// These "field" classes represent entries in a DialogModel. They are owned
// by the model and either created through the model or DialogModel::Builder.
// These entries can be referred to by setting the field's unique id in
// construction parameters (::Params::SetUniqueId()). They can then later be
// acquired through DialogModel::GetFieldByUniqueId() methods.
// These fields own the data corresponding to their field. For instance, the
// text of a textfield in a model is read using DialogModelTextfield::text() and
// stays in sync with the visible dialog (through DialogModelHosts).
class COMPONENT_EXPORT(UI_BASE) DialogModelField {
public:
enum Type { kButton, kTextfield, kCombobox };
DialogModelField(const DialogModelField&) = delete;
DialogModelField& operator=(const DialogModelField&) = delete;
virtual ~DialogModelField();
// Accessors with util::PassKey<DialogModelHost> are only intended to be read
// by the DialogModelHost implementation.
const base::flat_set<Accelerator>& accelerators(
util::PassKey<DialogModelHost>) const {
return accelerators_;
}
// Reserved ID generated by the model. This ID is generated for every field
// and used to map from DialogModelHost fields (like views::Textfield) to
// DialogModelFields (like DialogModelTextfield).
// WARNING: This should not be confused with the ID set by subclasses'
// Params::SetUniqueId() methods.
// TODO(pbos): See if this int-to-field mapping can be replaced with using
// DialogModelField pointers in DialogModelHost. This is currently only used
// because views has a convenient views::View::SetID() function.
int model_field_id(util::PassKey<DialogModelHost>) const {
return model_field_id_;
}
Type type(util::PassKey<DialogModelHost>) const { return type_; }
protected:
// Struct that holds a reserved ID for the field in the model. This is
// protected to be able to be passed from DialogModelField children to the
// parent constructor. Its members are private because only DialogModel,
// DialogModelField and DialogModelHost should be able to read them.
// TODO(pbos): Reconsider whether this |model_field_id| can be avoided. This
// would take rewiring DialogModelHost to own a mapping between
// "DialogModelField*" and host classes (such as View) and not rely on things
// like View::SetID(model_field_id) to maintain that mapping. It would also
// require special handling of special buttons like DIALOG_BUTTON_OK.
struct Reservation {
private:
friend class DialogModel;
friend class DialogModelField;
// This is only to be constructed by DialogModel who makes the actual
// ID assignment.
Reservation(DialogModel* model, int model_field_id);
DialogModel* const model;
const int model_field_id;
};
DialogModelField(const Reservation& reservation,
Type type,
int unique_id,
base::flat_set<Accelerator> accelerators);
int model_field_id() const { return model_field_id_; }
private:
friend class DialogModel;
DialogModel* const model_;
const int model_field_id_;
const Type type_;
const int unique_id_;
const base::flat_set<Accelerator> accelerators_;
};
// Field class representing a dialog button.
class COMPONENT_EXPORT(UI_BASE) DialogModelButton : public DialogModelField {
public:
class COMPONENT_EXPORT(UI_BASE) Params {
public:
Params();
Params(const Params&) = delete;
Params& operator=(const Params&) = delete;
~Params();
Params& SetUniqueId(int unique_id);
// The button callback gets called when the button is activated. Whether
// that happens on key-press, release, etc. is implementation (and platform)
// dependent.
Params& SetCallback(base::RepeatingCallback<void(const Event&)> callback);
Params& AddAccelerator(Accelerator accelerator);
Params& SetAccessibleName(base::string16 accessible_name);
bool has_callback() const { return !!callback_; }
private:
friend class DialogModelButton;
int unique_id_ = -1;
base::RepeatingCallback<void(const Event&)> callback_;
base::flat_set<Accelerator> accelerators_;
};
// Note that this is constructed through a DialogModel (Reservation can't
// be created without one).
DialogModelButton(const Reservation& reservation,
base::string16 label,
const Params& params);
DialogModelButton(const DialogModelButton&) = delete;
DialogModelButton& operator=(const DialogModelButton&) = delete;
~DialogModelButton() override;
const base::string16& label() const { return label_; }
private:
friend class DialogModel;
const base::string16 label_;
base::RepeatingCallback<void(const Event&)> callback_;
};
// Field class representing a combobox and corresponding label to describe the
// combobox:
//
// <label> [combobox]
// Ex: Folder [My Bookmarks]
class COMPONENT_EXPORT(UI_BASE) DialogModelCombobox : public DialogModelField {
public:
class COMPONENT_EXPORT(UI_BASE) Params {
public:
Params();
Params(const Params&) = delete;
Params& operator=(const Params&) = delete;
~Params();
Params& SetUniqueId(int unique_id);
Params& AddAccelerator(Accelerator accelerator);
Params& SetAccessibleName(base::string16 accessible_name);
// The combobox callback is invoked when an item has been selected. This
// nominally happens when selecting an item in the combobox menu. The
// selection notably does not change by hovering different items in the
// combobox menu or navigating it with up/down keys as long as the menu is
// open.
Params& SetCallback(base::RepeatingClosure callback);
private:
friend class DialogModelCombobox;
const base::string16 label_;
int unique_id_ = -1;
base::string16 accessible_name_;
base::RepeatingClosure callback_;
base::flat_set<Accelerator> accelerators_;
};
// Note that this is constructed through a DialogModel (Reservation can't
// be created without one).
DialogModelCombobox(const Reservation& reservation,
base::string16 label,
std::unique_ptr<ui::ComboboxModel> combobox_model,
const Params& params);
DialogModelCombobox(const DialogModelCombobox&) = delete;
DialogModelCombobox& operator=(const DialogModelCombobox&) = delete;
~DialogModelCombobox() override;
const base::string16& label() const { return label_; }
const base::string16& accessible_name() const { return accessible_name_; }
int selected_index() const { return selected_index_; }
ui::ComboboxModel* combobox_model() { return combobox_model_.get(); }
private:
friend class DialogModel;
const base::string16 label_;
const base::string16 accessible_name_;
int selected_index_;
std::unique_ptr<ui::ComboboxModel> combobox_model_;
base::RepeatingClosure callback_;
};
// Field class representing a textfield and corresponding label to describe the
// textfield:
//
// <label> [textfield]
// Ex: Name [My email]
class COMPONENT_EXPORT(UI_BASE) DialogModelTextfield : public DialogModelField {
public:
class COMPONENT_EXPORT(UI_BASE) Params {
public:
Params();
Params(const Params&) = delete;
Params& operator=(const Params&) = delete;
~Params();
Params& SetUniqueId(int unique_id);
Params& AddAccelerator(Accelerator accelerator);
Params& SetAccessibleName(base::string16 accessible_name);
private:
friend class DialogModelTextfield;
int unique_id_ = -1;
base::string16 accessible_name_;
base::flat_set<Accelerator> accelerators_;
};
// Note that this is constructed through a DialogModel (Reservation can't
// be created without one).
DialogModelTextfield(const DialogModelField::Reservation& reservation,
base::string16 label,
base::string16 text,
const Params& params);
DialogModelTextfield(const DialogModelTextfield&) = delete;
DialogModelTextfield& operator=(const DialogModelTextfield&) = delete;
~DialogModelTextfield() override;
const base::string16& label() const { return label_; }
const base::string16& accessible_name() const { return accessible_name_; }
const base::string16& text() const { return text_; }
private:
friend class DialogModel;
const base::string16 label_;
const base::string16 accessible_name_;
base::string16 text_;
};
} // namespace ui
#endif // UI_BASE_MODELS_DIALOG_MODEL_FIELD_H_
\ No newline at end of file
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_BASE_MODELS_DIALOG_MODEL_HOST_H_
#define UI_BASE_MODELS_DIALOG_MODEL_HOST_H_
#include "base/util/type_safety/pass_key.h"
namespace ui {
class DialogModel;
// Platform-agnostic interface for toolkit integrations.
class COMPONENT_EXPORT(UI_BASE) DialogModelHost {
public:
// TODO(pbos): Try to get Close semantically synchronous (guarantee
// synchronous destruction of model), so it cannot be observed as
// asynchronous even if GetWidget()->Close() under Views is async.
virtual void Close() = 0;
// Selects all text of a textfield.
// TODO(pbos): Reconsider whether this should be implied by if the textfield
// is initially focused.
virtual void SelectAllText(int unique_id) = 0;
protected:
friend class DialogModel;
friend class DialogModelField;
// This PassKey is used to make sure that some methods on DialogModel
// are only called as part of the host integration.
util::PassKey<DialogModelHost> GetPassKey() {
return util::PassKey<DialogModelHost>();
}
// Called when various parts of the model changes.
// TODO(pbos): Break this down to API that says what was added/removed/changed
// to not have to reset everything.
virtual void OnModelChanged(DialogModel* model) = 0;
};
} // namespace ui
#endif // UI_BASE_MODELS_DIALOG_MODEL_HOST_H_
......@@ -100,6 +100,7 @@ component("views") {
"border.h",
"bubble/bubble_border.h",
"bubble/bubble_dialog_delegate_view.h",
"bubble/bubble_dialog_model_host.h",
"bubble/bubble_frame_view.h",
"bubble/info_bubble.h",
"bubble/tooltip_icon.h",
......@@ -312,6 +313,7 @@ component("views") {
"border.cc",
"bubble/bubble_border.cc",
"bubble/bubble_dialog_delegate_view.cc",
"bubble/bubble_dialog_model_host.cc",
"bubble/bubble_frame_view.cc",
"bubble/footnote_container_view.cc",
"bubble/info_bubble.cc",
......
This diff is collapsed.
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_VIEWS_BUBBLE_BUBBLE_DIALOG_MODEL_HOST_H_
#define UI_VIEWS_BUBBLE_BUBBLE_DIALOG_MODEL_HOST_H_
#include <memory>
#include <vector>
#include "base/callback.h"
#include "ui/base/models/dialog_model.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/combobox/combobox_listener.h"
namespace views {
class Combobox;
class GridLayout;
class Textfield;
// BubbleDialogModelHost is a views implementation of ui::DialogModelHost which
// hosts a ui::DialogModels as a BubbleDialogDelegateView. This exposes
// views-specific methods such as SetAnchorView(), SetArrow() and
// SetHighlightedButton(). For methods that are reflected in ui::DialogModelHost
// (such as ::Close()), preferusing the ui::DialogModelHost to avoid
// platform-specific code (GetWidget()->Close()) where unnecessary. For those
// methods, note that this can be retrieved as a ui::DialogModelHost through
// DialogModel::host(). This helps minimize platform-specific code from
// platform-agnostic model-delegate code.
class VIEWS_EXPORT BubbleDialogModelHost : public BubbleDialogDelegateView,
public ui::DialogModelHost,
public ButtonListener,
public ComboboxListener {
public:
// Constructs a BubbleDialogModelHost, which for most purposes is to used as a
// BubbleDialogDelegateView. The BubbleDialogDelegateView is nominally handed
// to BubbleDialogDelegateView::CreateBubble() which returns a Widget that has
// taken ownership of the bubble. Widget::Show() finally shows the bubble.
explicit BubbleDialogModelHost(std::unique_ptr<ui::DialogModel> model);
~BubbleDialogModelHost() override;
// BubbleDialogDelegateView:
// TODO(pbos): Populate initparams with initial view instead of overriding
// GetInitiallyFocusedView().
View* GetInitiallyFocusedView() override;
void OnDialogInitialized() override;
// ui::DialogModelHost:
void Close() override;
void SelectAllText(int unique_id) override;
void OnModelChanged(ui::DialogModel* model) override;
// ButtonListener:
void ButtonPressed(views::Button* sender, const ui::Event& event) override;
// ComboboxListener:
void OnPerformAction(views::Combobox* combobox) override;
private:
GridLayout* GetGridLayout();
void ConfigureGridLayout();
Textfield* AddOrUpdateTextfield(const ui::DialogModelTextfield& field);
Combobox* AddOrUpdateCombobox(ui::DialogModelCombobox* field);
void AddLabelAndField(const base::string16& label_text,
std::unique_ptr<views::View> field,
const gfx::FontList& field_font);
void UpdateAccelerators();
void NotifyTextfieldTextChanged(int id, views::Textfield* textfield);
void NotifyComboboxSelectedIndexChanged(int id, views::Combobox* combobox);
std::unique_ptr<ui::DialogModel> model_;
std::vector<PropertyChangedSubscription> property_changed_subscriptions_;
};
} // namespace views
#endif // UI_VIEWS_BUBBLE_BUBBLE_DIALOG_MODEL_HOST_H_
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