Skip to content

Commit

Permalink
Add a DialogExample example to views_examples.
Browse files Browse the repository at this point in the history
See http://crbug.com/671820#c10 for how it looks.

This is helpful for demonstrating behaviours and experimenting with
layout, e.g., for long strings.

BUG=671820

Review-Url: https://codereview.chromium.org/2701373002
Cr-Commit-Position: refs/heads/master@{#452311}
  • Loading branch information
tapted authored and Commit bot committed Feb 23, 2017
1 parent 99fc4dd commit b835074
Show file tree
Hide file tree
Showing 4 changed files with 429 additions and 0 deletions.
2 changes: 2 additions & 0 deletions ui/views/examples/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ component("views_examples_lib") {
"checkbox_example.h",
"combobox_example.cc",
"combobox_example.h",
"dialog_example.cc",
"dialog_example.h",
"example_base.cc",
"example_base.h",
"example_combobox_model.cc",
Expand Down
329 changes: 329 additions & 0 deletions ui/views/examples/dialog_example.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,329 @@
// 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);
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::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;

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::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,
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
Loading

0 comments on commit b835074

Please sign in to comment.