blob: 7b5ce6b74a5830982ba0106541abc75bd5a84384 [file] [log] [blame]
// 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_view.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_provider.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() {
this->SetLayoutManager(std::make_unique<FillLayout>());
Label* body = new Label(parent_->body_->text());
body->SetMultiLine(true);
body->SetHorizontalAlignment(gfx::ALIGN_LEFT);
body->SetBackground(CreateSolidBackground(SkColorSetRGB(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() override {
if (!parent_->has_extra_button_->checked())
return nullptr;
return MdTextButton::CreateSecondaryUiButton(
nullptr, parent_->extra_button_label_->text());
}
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) {}
// WidgetDelegate:
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;
views::LayoutProvider* provider = views::LayoutProvider::Get();
const int horizontal_spacing =
provider->GetDistanceMetric(views::DISTANCE_RELATED_BUTTON_HORIZONTAL);
GridLayout* layout = container->SetLayoutManager(
std::make_unique<views::GridLayout>(container));
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,
provider->GetDistanceMetric(views::DISTANCE_UNRELATED_CONTROL_VERTICAL));
show_ =
MdTextButton::CreateSecondaryUiButton(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,
views::LayoutProvider::Get()->GetDistanceMetric(
views::DISTANCE_RELATED_CONTROL_VERTICAL));
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(), 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);
// For user-resizable dialogs, ensure the window manager enforces any new
// minimum size.
widget->OnSizeConstraintsChanged();
}
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_->DialogModelChanged();
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_->DialogModelChanged();
}
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