Chromium Code Reviews| Index: ui/views/examples/dialog_example.cc |
| diff --git a/ui/views/examples/dialog_example.cc b/ui/views/examples/dialog_example.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..f1f79740576a9e43d595e524ec8b560259feb5d5 |
| --- /dev/null |
| +++ b/ui/views/examples/dialog_example.cc |
| @@ -0,0 +1,327 @@ |
| +// Copyright 2017 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/views/examples/dialog_example.h" |
| + |
| +#include "base/macros.h" |
| +#include "base/strings/utf_string_conversions.h" |
| +#include "ui/views/bubble/bubble_dialog_delegate.h" |
| +#include "ui/views/controls/button/checkbox.h" |
| +#include "ui/views/controls/button/label_button.h" |
| +#include "ui/views/controls/button/md_text_button.h" |
| +#include "ui/views/controls/combobox/combobox.h" |
| +#include "ui/views/controls/label.h" |
| +#include "ui/views/controls/textfield/textfield.h" |
| +#include "ui/views/layout/fill_layout.h" |
| +#include "ui/views/layout/grid_layout.h" |
| +#include "ui/views/layout/layout_constants.h" |
| +#include "ui/views/views_delegate.h" |
| +#include "ui/views/widget/widget.h" |
| +#include "ui/views/window/dialog_client_view.h" |
| + |
| +using base::ASCIIToUTF16; |
| + |
| +namespace views { |
| +namespace examples { |
| +namespace { |
| + |
| +constexpr int kFieldsColumnId = 0; |
| +constexpr int kButtonsColumnId = 1; |
| +constexpr int kFakeModeless = ui::MODAL_TYPE_SYSTEM + 1; |
| + |
| +} // namespace |
| + |
| +template <class DialogType> |
| +class DialogExample::Delegate : public virtual DialogType { |
| + public: |
| + explicit Delegate(DialogExample* parent) : parent_(parent) {} |
| + |
| + void InitDelegate() { |
| + LayoutManager* fill_layout = new FillLayout(); |
| + this->SetLayoutManager(fill_layout); |
|
Patti Lor
2017/02/22 05:58:34
Is the "this->" needed here? (and below?)
tapted
2017/02/22 22:45:30
Yup - because the parent is a template argument, t
|
| + Label* body = new Label(parent_->body_->text()); |
| + body->SetMultiLine(true); |
| + body->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
| + body->set_background(Background::CreateSolidBackground(0, 255, 255)); |
| + this->AddChildView(body); |
| + |
| + // Give the example code a way to change the body text. |
| + parent_->last_body_label_ = body; |
| + } |
| + |
| + protected: |
| + // WidgetDelegate: |
| + ui::ModalType GetModalType() const override { |
| + return parent_->GetModalType(); |
| + } |
| + |
| + base::string16 GetWindowTitle() const override { |
| + return parent_->title_->text(); |
| + } |
| + |
| + // DialogDelegate: |
| + View* CreateExtraView() { |
| + if (!parent_->has_extra_button_->checked()) |
| + return nullptr; |
| + return MdTextButton::Create(nullptr, parent_->extra_button_label_->text()); |
|
Patti Lor
2017/02/22 05:58:34
I realise this is for testing MD/Harmony stuff, bu
tapted
2017/02/22 22:45:30
ooh good point - this should use MdTextButton::Cre
|
| + } |
| + |
| + bool Cancel() override { return parent_->AllowDialogClose(false); } |
| + bool Accept() override { return parent_->AllowDialogClose(true); } |
| + int GetDialogButtons() const override { return parent_->GetDialogButtons(); } |
| + base::string16 GetDialogButtonLabel(ui::DialogButton button) const override { |
| + if (button == ui::DIALOG_BUTTON_OK) |
| + return parent_->ok_button_label_->text(); |
| + if (button == ui::DIALOG_BUTTON_CANCEL) |
| + return parent_->cancel_button_label_->text(); |
| + return base::string16(); |
| + } |
| + |
| + private: |
| + DialogExample* parent_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(Delegate); |
| +}; |
| + |
| +class DialogExample::Bubble : public Delegate<BubbleDialogDelegateView> { |
| + public: |
| + Bubble(DialogExample* parent, View* anchor) |
| + : BubbleDialogDelegateView(anchor, BubbleBorder::TOP_LEFT), |
| + Delegate(parent) { |
| + set_close_on_deactivate(!parent->persistent_bubble_->checked()); |
| + } |
| + |
| + // BubbleDialogDelegateView: |
| + void Init() override { InitDelegate(); } |
| + |
| + private: |
| + DISALLOW_COPY_AND_ASSIGN(Bubble); |
| +}; |
| + |
| +class DialogExample::Dialog : public Delegate<DialogDelegateView> { |
| + public: |
| + explicit Dialog(DialogExample* parent) : Delegate(parent) {} |
| + |
| + // WidgetDelgate: |
|
Patti Lor
2017/02/22 05:58:34
WidgetDelegate (typo)
tapted
2017/02/22 22:45:30
Done.
|
| + bool CanResize() const override { |
| + // Mac supports resizing of modal dialogs (parent or window-modal). On other |
| + // platforms this will be weird unless the modal type is "none", but helps |
| + // test layout. |
| + return true; |
| + } |
| + |
| + private: |
| + DISALLOW_COPY_AND_ASSIGN(Dialog); |
| +}; |
| + |
| +DialogExample::DialogExample() |
| + : ExampleBase("Dialog"), |
| + mode_model_({ |
| + base::ASCIIToUTF16("Modeless"), base::ASCIIToUTF16("Window Modal"), |
| + base::ASCIIToUTF16("Child Modal"), base::ASCIIToUTF16("System Modal"), |
| + base::ASCIIToUTF16("Fake Modeless (non-bubbles)"), |
| + }) {} |
| + |
| +DialogExample::~DialogExample() {} |
| + |
| +void DialogExample::CreateExampleView(View* container) { |
| + // GridLayout |resize_percent| constants. |
| + const float kFixed = 0.f; |
| + const float kStretchy = 1.f; |
| + |
| + const int horizontal_spacing = |
| + ViewsDelegate::GetInstance()->GetDialogRelatedButtonHorizontalSpacing(); |
| + GridLayout* layout = GridLayout::CreatePanel(container); |
| + container->SetLayoutManager(layout); |
| + ColumnSet* column_set = layout->AddColumnSet(kFieldsColumnId); |
| + column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, kFixed, |
| + GridLayout::USE_PREF, 0, 0); |
| + column_set->AddPaddingColumn(kFixed, horizontal_spacing); |
| + column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, kStretchy, |
| + GridLayout::USE_PREF, 0, 0); |
| + column_set->AddPaddingColumn(kFixed, horizontal_spacing); |
| + column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, kFixed, |
| + GridLayout::USE_PREF, 0, 0); |
| + StartTextfieldRow(layout, &title_, "Dialog Title", "Title"); |
| + StartTextfieldRow(layout, &body_, "Dialog Body Text", "Body Text"); |
| + |
| + StartTextfieldRow(layout, &ok_button_label_, "OK Button Label", "Done"); |
| + AddCheckbox(layout, &has_ok_button_); |
| + |
| + StartTextfieldRow(layout, &cancel_button_label_, "Cancel Button Label", |
| + "Cancel"); |
| + AddCheckbox(layout, &has_cancel_button_); |
| + |
| + StartTextfieldRow(layout, &extra_button_label_, "Extra Button Label", "Edit"); |
| + AddCheckbox(layout, &has_extra_button_); |
| + |
| + StartRowWithLabel(layout, "Modal Type"); |
| + mode_ = new Combobox(&mode_model_); |
| + mode_->set_listener(this); |
| + mode_->SetSelectedIndex(ui::MODAL_TYPE_CHILD); |
| + layout->AddView(mode_); |
| + |
| + StartRowWithLabel(layout, "Bubble"); |
| + AddCheckbox(layout, &bubble_); |
| + AddCheckbox(layout, &persistent_bubble_); |
| + persistent_bubble_->SetText(base::ASCIIToUTF16("Persistent")); |
| + |
| + column_set = layout->AddColumnSet(kButtonsColumnId); |
| + column_set->AddColumn(GridLayout::CENTER, GridLayout::CENTER, kStretchy, |
| + GridLayout::USE_PREF, 0, 0); |
| + layout->StartRowWithPadding(kFixed, kButtonsColumnId, kFixed, |
| + kUnrelatedControlVerticalSpacing); |
| + show_ = MdTextButton::Create(this, base::ASCIIToUTF16("Show")); |
| + layout->AddView(show_); |
| + |
| + // Grow the dialog a bit when this example is first selected, so it all fits. |
| + gfx::Size dialog_size = container->GetWidget()->GetRestoredBounds().size(); |
| + dialog_size.set_height(dialog_size.height() + 80); |
| + container->GetWidget()->SetSize(dialog_size); |
| +} |
| + |
| +void DialogExample::StartRowWithLabel(GridLayout* layout, const char* label) { |
| + const float kFixedVerticalResize = 0.f; |
| + layout->StartRowWithPadding( |
| + kFixedVerticalResize, kFieldsColumnId, kFixedVerticalResize, |
| + ViewsDelegate::GetInstance()->GetDialogRelatedControlVerticalSpacing()); |
| + layout->AddView(new Label(base::ASCIIToUTF16(label))); |
| +} |
| + |
| +void DialogExample::StartTextfieldRow(GridLayout* layout, |
| + Textfield** member, |
| + const char* label, |
| + const char* value) { |
| + StartRowWithLabel(layout, label); |
| + Textfield* textfield = new Textfield(); |
| + layout->AddView(textfield); |
| + textfield->set_controller(this); |
| + textfield->SetText(base::ASCIIToUTF16(value)); |
| + *member = textfield; |
| +} |
| + |
| +void DialogExample::AddCheckbox(GridLayout* layout, Checkbox** member) { |
| + Checkbox* checkbox = new Checkbox(base::string16()); |
| + checkbox->set_listener(this); |
| + checkbox->SetChecked(true); |
| + layout->AddView(checkbox); |
| + *member = checkbox; |
| +} |
| + |
| +ui::ModalType DialogExample::GetModalType() const { |
| + // "Fake" modeless happens when a DialogDelegate specifies window-modal, but |
| + // doesn't provide a parent window. |
| + if (mode_->selected_index() == kFakeModeless) |
| + return ui::MODAL_TYPE_WINDOW; |
| + |
| + return static_cast<ui::ModalType>(mode_->selected_index()); |
| +} |
| + |
| +int DialogExample::GetDialogButtons() const { |
| + int buttons = 0; |
| + if (has_ok_button_->checked()) |
| + buttons |= ui::DIALOG_BUTTON_OK; |
| + if (has_cancel_button_->checked()) |
| + buttons |= ui::DIALOG_BUTTON_CANCEL; |
| + return buttons; |
| +} |
| + |
| +bool DialogExample::AllowDialogClose(bool accept) { |
| + PrintStatus("Dialog closed with %s.", accept ? "Accept" : "Cancel"); |
| + last_dialog_ = nullptr; |
| + last_body_label_ = nullptr; |
| + return true; |
| +} |
| + |
| +void DialogExample::ResizeDialog() { |
| + DCHECK(last_dialog_); |
| + Widget* widget = last_dialog_->GetWidget(); |
| + gfx::Rect preferred_bounds(widget->GetRestoredBounds()); |
| + preferred_bounds.set_size(widget->non_client_view()->GetPreferredSize()); |
| + |
| + // Q: Do we need NonClientFrameView::GetWindowBoundsForClientBounds() here? |
| + // A: When DialogCientView properly feeds back sizes, we do not. |
| + widget->SetBoundsConstrained(preferred_bounds); |
| +} |
| + |
| +void DialogExample::ButtonPressed(Button* sender, const ui::Event& event) { |
| + if (sender == show_) { |
| + if (bubble_->checked()) { |
| + Bubble* bubble = new Bubble(this, sender); |
| + last_dialog_ = bubble; |
| + BubbleDialogDelegateView::CreateBubble(bubble); |
| + } else { |
| + Dialog* dialog = new Dialog(this); |
| + last_dialog_ = dialog; |
| + dialog->InitDelegate(); |
| + |
| + // constrained_window::CreateBrowserModalDialogViews() allows dialogs to |
| + // be created as MODAL_TYPE_WINDOW without specifying a parent. |
| + gfx::NativeView parent = nullptr; |
| + if (mode_->selected_index() != kFakeModeless) |
| + parent = container()->GetWidget()->GetNativeView(); |
| + |
| + DialogDelegate::CreateDialogWidget( |
| + dialog, container()->GetWidget()->GetNativeWindow(), parent); |
| + } |
| + last_dialog_->GetWidget()->Show(); |
| + return; |
| + } |
| + |
| + if (sender == bubble_) { |
| + if (bubble_->checked() && GetModalType() != ui::MODAL_TYPE_CHILD) { |
| + mode_->SetSelectedIndex(ui::MODAL_TYPE_CHILD); |
| + PrintStatus("You nearly always want Child Modal for bubbles."); |
| + } |
| + persistent_bubble_->SetEnabled(bubble_->checked()); |
| + OnPerformAction(mode_); // Validate the modal type. |
| + |
| + if (!bubble_->checked() && GetModalType() == ui::MODAL_TYPE_CHILD) { |
| + // Do something reasonable when simply unchecking bubble and re-enable. |
| + mode_->SetSelectedIndex(ui::MODAL_TYPE_WINDOW); |
| + OnPerformAction(mode_); |
| + } |
| + return; |
| + } |
| + |
| + // Other buttons are all checkboxes. Update the dialog if there is one. |
| + if (last_dialog_) { |
| + last_dialog_->GetDialogClientView()->UpdateDialogButtons(); |
| + ResizeDialog(); |
| + } |
| +} |
| + |
| +void DialogExample::ContentsChanged(Textfield* sender, |
| + const base::string16& new_contents) { |
| + if (!last_dialog_) |
| + return; |
| + |
| + if (sender == extra_button_label_) |
| + PrintStatus("DialogClientView can never refresh the extra view."); |
| + |
| + if (sender == title_) { |
| + last_dialog_->GetWidget()->UpdateWindowTitle(); |
| + } else if (sender == body_) { |
| + last_body_label_->SetText(new_contents); |
| + } else { |
| + last_dialog_->GetDialogClientView()->UpdateDialogButtons(); |
| + } |
| + |
| + ResizeDialog(); |
| +} |
| + |
| +void DialogExample::OnPerformAction(Combobox* combobox) { |
| + bool enable = bubble_->checked() || GetModalType() != ui::MODAL_TYPE_CHILD; |
| +#if defined(OS_MACOSX) |
| + enable = enable && GetModalType() != ui::MODAL_TYPE_SYSTEM; |
| +#endif |
| + show_->SetEnabled(enable); |
| + if (!enable && GetModalType() == ui::MODAL_TYPE_CHILD) |
| + PrintStatus("MODAL_TYPE_CHILD can't be used with non-bubbles."); |
| + if (!enable && GetModalType() == ui::MODAL_TYPE_SYSTEM) |
| + PrintStatus("MODAL_TYPE_SYSTEM isn't supported on Mac."); |
| +} |
| + |
| +} // namespace examples |
| +} // namespace views |