| // Copyright 2025 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/views/examples/dialog_model_example.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/callback_list.h" |
| #include "base/functional/bind.h" |
| #include "base/logging.h" |
| #include "base/strings/strcat.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/models/combobox_model.h" |
| #include "ui/base/models/dialog_model.h" |
| #include "ui/views/bubble/bubble_dialog_model_host.h" |
| #include "ui/views/controls/button/checkbox.h" |
| #include "ui/views/controls/button/label_button.h" |
| #include "ui/views/controls/label.h" |
| #include "ui/views/examples/examples_window.h" |
| #include "ui/views/layout/box_layout_view.h" |
| #include "ui/views/widget/widget.h" |
| |
| namespace views::examples { |
| |
| namespace { |
| |
| // Identifiers for the fields in the dialog model. This is used to retrieve the |
| // field from the dialog model in response to user actions. |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kNameTextfield); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kPasswordField); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kRememberMeCheckbox); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kFruitCombobox); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kCustomField); |
| |
| // A combobox model that provides a list of fruits. |
| // Used by `DialogModel::Builder::AddCombobox()`. |
| class FruitsComboboxModel : public ui::ComboboxModel { |
| public: |
| FruitsComboboxModel() { |
| fruits_.push_back(u"Apple"); |
| fruits_.push_back(u"Banana"); |
| fruits_.push_back(u"Orange"); |
| } |
| |
| FruitsComboboxModel(const FruitsComboboxModel&) = delete; |
| FruitsComboboxModel& operator=(const FruitsComboboxModel&) = delete; |
| |
| ~FruitsComboboxModel() override = default; |
| |
| // ui::ComboboxModel: |
| size_t GetItemCount() const override { return fruits_.size(); } |
| std::u16string GetItemAt(size_t index) const override { |
| return fruits_[index]; |
| } |
| |
| private: |
| std::vector<std::u16string> fruits_; |
| }; |
| |
| // This delegate is owned by the dialog model. It provides access to the dialog |
| // model via the `dialog_model()` method. The client uses this to read the |
| // values of the fields. |
| class DialogModelExampleDelegate : public ui::DialogModelDelegate { |
| public: |
| DialogModelExampleDelegate() = default; |
| DialogModelExampleDelegate(const DialogModelExampleDelegate&) = delete; |
| DialogModelExampleDelegate& operator=(const DialogModelExampleDelegate&) = |
| delete; |
| ~DialogModelExampleDelegate() override = default; |
| |
| void SetCustomCheckbox(Checkbox* checkbox) { |
| if (!checkbox) { |
| custom_checkbox_checked_ = false; |
| custom_checkbox_checked_changed_subscription_ = |
| base::CallbackListSubscription(); |
| return; |
| } |
| |
| custom_checkbox_checked_ = checkbox->GetChecked(); |
| custom_checkbox_checked_changed_subscription_ = |
| checkbox->AddCheckedChangedCallback(base::BindRepeating( |
| &DialogModelExampleDelegate::OnCustomCheckboxCheckedChanged, |
| base::Unretained(this))); |
| } |
| |
| // This is called when the "OK" button is pressed. |
| // This is registered via `DialogModel::Builder::AddOkButton()`. |
| void OnDialogAccepted() { |
| std::u16string output = base::StrCat( |
| {u"Hello ", |
| dialog_model()->GetTextfieldByUniqueId(kNameTextfield)->text(), |
| u", your password is ", |
| dialog_model()->GetPasswordFieldByUniqueId(kPasswordField)->text(), |
| dialog_model() |
| ->GetCheckboxByUniqueId(kRememberMeCheckbox) |
| ->is_checked() |
| ? u". You want to be remembered." |
| : u".", |
| u"Your favorite fruit is ", |
| dialog_model() |
| ->GetComboboxByUniqueId(kFruitCombobox) |
| ->combobox_model() |
| ->GetItemAt(dialog_model() |
| ->GetComboboxByUniqueId(kFruitCombobox) |
| ->selected_index()), |
| custom_checkbox_checked_ ? u". Custom checkbox is checked!" : u"."}); |
| |
| // Print the status to the bottom of the dialog. |
| PrintStatus(base::UTF16ToUTF8(output)); |
| } |
| |
| // This is called when the "Cancel" button is pressed, or when the dialog is |
| // closed without pressing a button. |
| // This is registered via `DialogModel::Builder::AddCancelButton()`. |
| void OnDialogCancelled() { PrintStatus("Dialog cancelled."); } |
| |
| // This is called when the "Extra Button" is pressed. |
| // This is registered via `DialogModel::Builder::AddExtraButton()`. |
| void OnExtraButtonPressed(const ui::Event& event) { |
| PrintStatus("Extra button pressed."); |
| } |
| |
| private: |
| // This is called when the "Check me" checkbox is checked or unchecked. |
| // This is registered via `Checkbox::AddCheckedChangedCallback()`. |
| void OnCustomCheckboxCheckedChanged() { |
| custom_checkbox_checked_ = !custom_checkbox_checked_; |
| } |
| |
| bool custom_checkbox_checked_ = false; |
| base::CallbackListSubscription custom_checkbox_checked_changed_subscription_; |
| |
| base::WeakPtrFactory<DialogModelExampleDelegate> weak_ptr_factory_{this}; |
| }; |
| |
| } // namespace |
| |
| DialogModelExample::DialogModelExample() : ExampleBase("Dialog Model") {} |
| |
| DialogModelExample::~DialogModelExample() = default; |
| |
| void DialogModelExample::CreateExampleView(View* container) { |
| container->SetUseDefaultFillLayout(true); |
| |
| // Add a "Show Dialog" button that invokes `ShowDialog()` when clicked. |
| auto view = Builder<BoxLayoutView>() |
| .SetCrossAxisAlignment(BoxLayout::CrossAxisAlignment::kStart) |
| .AddChildren(Builder<MdTextButton>() |
| .CopyAddressTo(&show_dialog_button_) |
| .SetText(u"Show Dialog") |
| .SetCallback(base::BindRepeating( |
| &DialogModelExample::ShowDialog, |
| base::Unretained(this)))) |
| .Build(); |
| |
| container->AddChildView(std::move(view)); |
| } |
| |
| void DialogModelExample::ShowDialog() { |
| // Create a client-supplied model delegate. This is owned by the dialog model. |
| // The delegate's methods are registered as button callbacks via the dialog |
| // model builder. |
| auto model_delegate = std::make_unique<DialogModelExampleDelegate>(); |
| auto* model_delegate_ptr = model_delegate.get(); |
| |
| // Create a custom view with a checkbox. This view is later added to the |
| // dialog as a custom field via `DialogModel::Builder::AddCustomField()`. |
| auto custom_view_builder = |
| Builder<BoxLayoutView>() |
| .SetOrientation(BoxLayout::Orientation::kHorizontal) |
| .AddChildren(Builder<Label>().SetText(u"This is a custom field!")); |
| Checkbox* custom_checkbox = nullptr; |
| auto custom_view = std::move(custom_view_builder) |
| .AddChild(Builder<Checkbox>() |
| .SetText(u"Check me") |
| .CopyAddressTo(&custom_checkbox)) |
| .Build(); |
| model_delegate_ptr->SetCustomCheckbox(custom_checkbox); |
| |
| auto dialog_model = |
| ui::DialogModel::Builder(std::move(model_delegate)) |
| .SetTitle(u"Hello, world!") |
| .AddParagraph(ui::DialogModelLabel(u"This is a paragraph.")) |
| .AddTextfield(kNameTextfield, u"Name", u"") |
| .AddPasswordField(kPasswordField, u"Password", u"Password", u"") |
| .AddCheckbox(kRememberMeCheckbox, |
| ui::DialogModelLabel(u"Remember me")) |
| .AddSeparator() |
| .AddParagraph(ui::DialogModelLabel(u"This is another paragraph.")) |
| .AddCombobox(kFruitCombobox, u"Favorite Fruit", |
| std::make_unique<FruitsComboboxModel>()) |
| .AddCustomField(std::make_unique<BubbleDialogModelHost::CustomView>( |
| std::move(custom_view), |
| BubbleDialogModelHost::FieldType::kText), |
| kCustomField) |
| .AddOkButton( |
| base::BindOnce(&DialogModelExampleDelegate::OnDialogAccepted, |
| base::Unretained(model_delegate_ptr))) |
| .AddCancelButton( |
| base::BindOnce(&DialogModelExampleDelegate::OnDialogCancelled, |
| base::Unretained(model_delegate_ptr))) |
| .AddExtraButton( |
| base::BindRepeating( |
| &DialogModelExampleDelegate::OnExtraButtonPressed, |
| base::Unretained(model_delegate_ptr)), |
| ui::DialogModel::Button::Params().SetLabel(u"Extra Button")) |
| .SetFootnote(ui::DialogModelLabel(u"This is a footnote.")) |
| .Build(); |
| |
| // Creates a dialog host that anchors the dialog to the "Show Dialog" button. |
| auto bubble = std::make_unique<BubbleDialogModelHost>( |
| std::move(dialog_model), show_dialog_button_, BubbleBorder::TOP_LEFT); |
| |
| // Creates and shows the dialog. |
| BubbleDialogDelegate::CreateBubble(std::move(bubble))->Show(); |
| } |
| |
| } // namespace views::examples |