Chromium Code Reviews| Index: ui/views/examples/box_layout_example.cc |
| diff --git a/ui/views/examples/box_layout_example.cc b/ui/views/examples/box_layout_example.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..39f087a08ed6ce9d4106ce76f4fda9a44d324390 |
| --- /dev/null |
| +++ b/ui/views/examples/box_layout_example.cc |
| @@ -0,0 +1,419 @@ |
| +// Copyright (c) 2017 The Chromium Authors. All rights reserved. |
|
sky
2017/05/09 19:26:02
no (c)
|
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "box_layout_example.h" |
| + |
| +#include <vector> |
| + |
| +#include "base/memory/ptr_util.h" |
| +#include "base/strings/string16.h" |
| +#include "base/strings/string_number_conversions.h" |
| +#include "third_party/skia/include/core/SkColor.h" |
| +#include "ui/base/models/combobox_model.h" |
| +#include "ui/gfx/geometry/insets.h" |
| +#include "ui/gfx/geometry/point.h" |
| +#include "ui/gfx/geometry/rect.h" |
| +#include "ui/views/border.h" |
| +#include "ui/views/controls/button/checkbox.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/view.h" |
| + |
| +namespace views { |
| +namespace examples { |
| + |
| +namespace { |
| + |
| +class FullPanel : public View { |
|
sky
2017/05/09 19:26:02
Please add comments for classes. This name is conf
kylix_rd
2017/05/10 15:30:36
Done.
|
| + public: |
| + FullPanel() {} |
| + ~FullPanel() override {} |
| + |
| + void Layout() override; |
|
sky
2017/05/09 19:26:03
Prefix with where override comes from.
kylix_rd
2017/05/10 15:30:36
Done.
|
| + |
| + private: |
| + DISALLOW_COPY_AND_ASSIGN(FullPanel); |
| +}; |
| + |
| +class ChildPanel : public View, public TextfieldController { |
| + public: |
| + ChildPanel(BoxLayoutExample* example); |
|
sky
2017/05/09 19:26:02
explicit.
kylix_rd
2017/05/10 15:30:36
Done.
|
| + ~ChildPanel() override {} |
| + |
| + // View |
| + gfx::Size GetPreferredSize() const override; |
| + bool OnMousePressed(const ui::MouseEvent& event) override; |
| + void Layout() override; |
| + |
| + void set_listener(ViewListener* listener) { listener_ = listener; } |
| + void SetSelected(bool value); |
| + bool selected() const { return selected_; }; |
| + |
| + int GetFlex(); |
| + |
| + private: |
| + // TextfieldController |
| + void ContentsChanged(Textfield* sender, |
| + const base::string16& new_contents) override; |
| + |
| + Textfield* CreateTextfield(); |
| + |
| + BoxLayoutExample* example_; |
| + bool selected_ = false; |
| + ViewListener* listener_ = nullptr; |
| + Textfield* flex_; |
| + Textfield* margin_[4]; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(ChildPanel); |
| +}; |
| + |
| +class ExampleModel : public ui::ComboboxModel { |
|
sky
2017/05/09 19:26:03
Can you use ui/views/examples/example_combobox_mod
kylix_rd
2017/05/10 15:30:36
Yep. I didn't notice it was there.
Done.
|
| + public: |
| + ExampleModel(const std::initializer_list<base::string16>& items) |
| + : ui::ComboboxModel(), items_(items) {} |
| + ~ExampleModel() override {} |
| + // ComboboxModel |
|
sky
2017/05/09 19:26:02
newline between 78/79.
|
| + int GetItemCount() const override { return items_.size(); } |
| + |
| + base::string16 GetItemAt(int index) override { return items_[index]; } |
| + |
| + private: |
| + std::vector<base::string16> items_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(ExampleModel); |
| +}; |
| + |
| +void FullPanel::Layout() { |
| + DCHECK_EQ(child_count(), 2); |
| + View* left_panel = child_at(0); |
| + View* right_panel = child_at(1); |
| + gfx::Rect bounds = GetContentsBounds(); |
| + left_panel->SetBounds(bounds.x(), bounds.y(), (bounds.width() * 75) / 100, |
| + bounds.height()); |
| + right_panel->SetBounds(left_panel->width(), bounds.y(), |
| + bounds.width() - left_panel->width(), bounds.height()); |
| +} |
| + |
| +ChildPanel::ChildPanel(BoxLayoutExample* example) : View(), example_(example) { |
| + SetBorder(CreateSolidBorder(1, SK_ColorGRAY)); |
| + for (unsigned i = 0; i < sizeof(margin_) / sizeof(margin_[0]); ++i) |
| + margin_[i] = CreateTextfield(); |
| + flex_ = CreateTextfield(); |
| + flex_->SetText(L""); |
| +} |
| + |
| +gfx::Size ChildPanel::GetPreferredSize() const { |
| + return gfx::Size(180, 90); |
| +} |
| + |
| +bool ChildPanel::OnMousePressed(const ui::MouseEvent& event) { |
| + if (event.IsOnlyLeftMouseButton()) |
| + SetSelected(true); |
| + return true; |
| +} |
| + |
| +void ChildPanel::Layout() { |
| + const int kSpacing = 2; |
| + if (selected_) { |
| + gfx::Rect client = GetContentsBounds(); |
| + for (unsigned i = 0; i < sizeof(margin_) / sizeof(margin_[0]); ++i) { |
| + gfx::Point pos; |
| + Textfield* textfield = margin_[i]; |
| + switch (i) { |
| + case 0: |
| + pos = gfx::Point((client.width() - textfield->width()) / 2, kSpacing); |
| + break; |
| + case 1: |
| + pos = |
| + gfx::Point(kSpacing, (client.height() - textfield->height()) / 2); |
| + break; |
| + case 2: |
| + pos = gfx::Point((client.width() - textfield->width()) / 2, |
| + client.height() - textfield->height() - kSpacing); |
| + break; |
| + case 3: |
| + pos = gfx::Point(client.width() - textfield->width() - kSpacing, |
| + (client.height() - textfield->height()) / 2); |
| + break; |
| + default: |
| + NOTREACHED(); |
| + } |
| + textfield->SetPosition(pos); |
| + } |
| + flex_->SetPosition(gfx::Point((client.width() - flex_->width()) / 2, |
| + (client.height() - flex_->height()) / 2)); |
| + } |
| +} |
| + |
| +void ChildPanel::SetSelected(bool value) { |
| + if (value != selected_) { |
| + selected_ = value; |
| + SetBorder(CreateSolidBorder(1, selected_ ? SK_ColorBLACK : SK_ColorGRAY)); |
| + if (selected_ && parent()) { |
| + for (int i = 0; i < parent()->child_count(); ++i) { |
| + View* child = parent()->child_at(i); |
| + if (child != this && child->GetGroup() == GetGroup()) { |
| + ChildPanel* child_panel = static_cast<ChildPanel*>(child); |
| + child_panel->SetSelected(false); |
| + } |
| + } |
| + if (listener_) |
| + listener_->ViewSelected(this); |
| + } |
| + for (Textfield* textfield : margin_) |
| + textfield->SetVisible(selected_); |
| + flex_->SetVisible(selected_); |
| + InvalidateLayout(); |
| + example_->RefreshLayoutPanel(); |
| + } |
| +} |
| + |
| +int ChildPanel::GetFlex() { |
| + int flex; |
| + if (base::StringToInt(flex_->text(), &flex)) |
| + return flex; |
| + return -1; |
| +} |
| + |
| +void ChildPanel::ContentsChanged(Textfield* sender, |
| + const base::string16& new_contents) { |
| + int edges[4]; |
| + for (unsigned i = 0; i < sizeof(margin_) / sizeof(margin_[0]); ++i) { |
| + base::StringToInt(margin_[i]->text(), &edges[i]); |
| + } |
| + gfx::Insets margins = gfx::Insets(edges[0], edges[1], edges[2], edges[3]); |
| + if (!margins.IsEmpty()) |
| + this->SetProperty(kMarginsKey, new gfx::Insets(margins)); |
| + else |
| + this->ClearProperty(kMarginsKey); |
| + if (sender == flex_) |
| + example_->UpdateLayoutManager(); |
| + example_->RefreshLayoutPanel(); |
| +} |
| + |
| +Textfield* ChildPanel::CreateTextfield() { |
| + Textfield* textfield = new Textfield(); |
| + textfield->set_default_width_in_chars(3); |
| + textfield->SizeToPreferredSize(); |
| + textfield->SetText(L"0"); |
| + textfield->set_controller(this); |
| + textfield->SetVisible(false); |
| + AddChildView(textfield); |
| + return textfield; |
| +} |
| +} |
| + |
| +BoxLayoutExample::BoxLayoutExample() : ExampleBase("Box Layout") {} |
| + |
| +BoxLayoutExample::~BoxLayoutExample() {} |
| + |
| +void BoxLayoutExample::CreateExampleView(View* container) { |
| + const int kSpacing = 5; |
| + const int kPadding = 8; |
| + container->SetLayoutManager(new FillLayout()); |
| + full_panel_ = new FullPanel(); |
| + container->AddChildView(full_panel_); |
| + |
| + box_layout_panel_ = new View(); |
| + box_layout_panel_->SetBorder(CreateSolidBorder(1, SK_ColorLTGRAY)); |
| + full_panel_->AddChildView(box_layout_panel_); |
| + control_panel_ = new View(); |
| + full_panel_->AddChildView(control_panel_); |
| + |
| + int vertical_pos = kPadding; |
| + add_button_ = MdTextButton::CreateSecondaryUiButton(this, L"Add"); |
| + add_button_->SetPosition(gfx::Point(kPadding, vertical_pos)); |
| + add_button_->SizeToPreferredSize(); |
| + control_panel_->AddChildView(add_button_); |
| + vertical_pos += add_button_->height() + kSpacing; |
| + |
| + Label* label = new Label(L"Orientation"); |
| + label->SetPosition(gfx::Point(kPadding, vertical_pos)); |
| + label->SizeToPreferredSize(); |
| + orientation_ = new Combobox(base::MakeUnique<ExampleModel>( |
| + std::initializer_list<base::string16>({L"Horizontal", L"Vertical"}))); |
| + orientation_->SetPosition( |
| + gfx::Point(label->x() + label->width() + kSpacing, vertical_pos)); |
| + orientation_->SizeToPreferredSize(); |
| + orientation_->set_listener(this); |
| + label->SetSize(gfx::Size(label->width(), orientation_->height())); |
| + control_panel_->AddChildView(label); |
| + control_panel_->AddChildView(orientation_); |
| + vertical_pos += orientation_->height() + kSpacing; |
| + |
| + label = new Label(L"Main axis"); |
| + label->SetPosition(gfx::Point(kPadding, vertical_pos)); |
| + label->SizeToPreferredSize(); |
| + main_axis_alignment_ = new Combobox(base::MakeUnique<ExampleModel>( |
| + std::initializer_list<base::string16>({L"Start", L"Center", L"End"}))); |
| + main_axis_alignment_->SetPosition( |
| + gfx::Point(label->x() + label->width() + kSpacing, vertical_pos)); |
| + main_axis_alignment_->SizeToPreferredSize(); |
| + main_axis_alignment_->set_listener(this); |
| + label->SetSize(gfx::Size(label->width(), main_axis_alignment_->height())); |
| + control_panel_->AddChildView(label); |
| + control_panel_->AddChildView(main_axis_alignment_); |
| + vertical_pos += main_axis_alignment_->height() + kSpacing; |
| + |
| + label = new Label(L"Cross axis"); |
| + label->SetPosition(gfx::Point(kPadding, vertical_pos)); |
| + label->SizeToPreferredSize(); |
| + cross_axis_alignment_ = new Combobox( |
| + base::MakeUnique<ExampleModel>(std::initializer_list<base::string16>( |
| + {L"Stretch", L"Start", L"Center", L"End"}))); |
| + cross_axis_alignment_->SetPosition( |
| + gfx::Point(label->x() + label->width() + kSpacing, vertical_pos)); |
| + cross_axis_alignment_->SizeToPreferredSize(); |
| + cross_axis_alignment_->set_listener(this); |
| + label->SetSize(gfx::Size(label->width(), cross_axis_alignment_->height())); |
| + control_panel_->AddChildView(label); |
| + control_panel_->AddChildView(cross_axis_alignment_); |
| + vertical_pos += cross_axis_alignment_->height() + kSpacing; |
| + |
| + label = new Label(L"Child spacing"); |
| + label->SetPosition(gfx::Point(kPadding, vertical_pos)); |
| + label->SizeToPreferredSize(); |
| + between_child_spacing_ = new Textfield(); |
| + between_child_spacing_->SetPosition( |
| + gfx::Point(label->x() + label->width() + kSpacing, vertical_pos)); |
| + between_child_spacing_->set_default_width_in_chars(3); |
| + between_child_spacing_->SetTextInputType(ui::TEXT_INPUT_TYPE_NUMBER); |
| + between_child_spacing_->SizeToPreferredSize(); |
| + between_child_spacing_->SetText(L"0"); |
| + between_child_spacing_->set_controller(this); |
| + label->SetSize(gfx::Size(label->width(), between_child_spacing_->height())); |
| + control_panel_->AddChildView(label); |
| + control_panel_->AddChildView(between_child_spacing_); |
| + vertical_pos += between_child_spacing_->height() + kSpacing; |
| + |
| + label = new Label(L"Default flex"); |
| + label->SetPosition(gfx::Point(kPadding, vertical_pos)); |
| + label->SizeToPreferredSize(); |
| + default_flex_ = new Textfield(); |
| + default_flex_->SetPosition( |
| + gfx::Point(label->x() + label->width() + kSpacing, vertical_pos)); |
| + default_flex_->set_default_width_in_chars(3); |
| + default_flex_->SetTextInputType(ui::TEXT_INPUT_TYPE_NUMBER); |
| + default_flex_->SizeToPreferredSize(); |
| + default_flex_->SetText(L"0"); |
| + default_flex_->set_controller(this); |
| + label->SetSize(gfx::Size(label->width(), default_flex_->height())); |
| + control_panel_->AddChildView(label); |
| + control_panel_->AddChildView(default_flex_); |
| + vertical_pos += default_flex_->height() + kSpacing; |
| + |
| + label = new Label(L"Min cross axis"); |
| + label->SetPosition(gfx::Point(kPadding, vertical_pos)); |
| + label->SizeToPreferredSize(); |
| + min_cross_axis_size_ = new Textfield(); |
| + min_cross_axis_size_->SetPosition( |
| + gfx::Point(label->x() + label->width() + kSpacing, vertical_pos)); |
| + min_cross_axis_size_->set_default_width_in_chars(3); |
| + min_cross_axis_size_->SetTextInputType(ui::TEXT_INPUT_TYPE_NUMBER); |
| + min_cross_axis_size_->SizeToPreferredSize(); |
| + min_cross_axis_size_->SetText(L"0"); |
| + min_cross_axis_size_->set_controller(this); |
| + label->SetSize(gfx::Size(label->width(), min_cross_axis_size_->height())); |
| + control_panel_->AddChildView(label); |
| + control_panel_->AddChildView(min_cross_axis_size_); |
| + vertical_pos += min_cross_axis_size_->height() + kSpacing; |
| + |
| + collapse_margins_ = new Checkbox(L"Collapse margins"); |
| + collapse_margins_->SetPosition(gfx::Point(kPadding, vertical_pos)); |
| + collapse_margins_->SizeToPreferredSize(); |
| + collapse_margins_->set_listener(this); |
| + control_panel_->AddChildView(collapse_margins_); |
| + |
| + UpdateLayoutManager(); |
| +} |
| + |
| +void BoxLayoutExample::ButtonPressed(Button* sender, const ui::Event& event) { |
| + if (sender == add_button_) { |
| + if (panel_count_ < 5) { |
| + ++panel_count_; |
| + ChildPanel* panel = new ChildPanel(this); |
| + panel->set_listener(this); |
| + panel->SetGroup(100); |
| + box_layout_panel_->AddChildView(panel); |
| + RefreshLayoutPanel(); |
| + } else { |
| + PrintStatus("Only 5 panels may be added"); |
| + } |
| + } else if (sender == collapse_margins_) { |
| + UpdateLayoutManager(); |
| + RefreshLayoutPanel(); |
| + } |
| +} |
| + |
| +void BoxLayoutExample::OnPerformAction(Combobox* combobox) { |
| + if (combobox == orientation_) { |
| + UpdateLayoutManager(); |
| + } else if (combobox == main_axis_alignment_) { |
| + layout_->set_main_axis_alignment(static_cast<BoxLayout::MainAxisAlignment>( |
| + main_axis_alignment_->selected_index())); |
| + } else if (combobox == cross_axis_alignment_) { |
| + layout_->set_cross_axis_alignment( |
| + static_cast<BoxLayout::CrossAxisAlignment>( |
| + cross_axis_alignment_->selected_index())); |
| + } |
| + RefreshLayoutPanel(); |
| +} |
| + |
| +void BoxLayoutExample::ViewSelected(View* view) {} |
| + |
| +void BoxLayoutExample::ContentsChanged(Textfield* textfield, |
| + const base::string16& new_contents) { |
| + if (textfield == between_child_spacing_) { |
| + UpdateLayoutManager(); |
| + } else if (textfield == default_flex_) { |
| + int default_flex; |
| + base::StringToInt(default_flex_->text(), &default_flex); |
| + layout_->SetDefaultFlex(default_flex); |
| + } else if (textfield == min_cross_axis_size_) { |
| + int min_cross_size; |
| + base::StringToInt(min_cross_axis_size_->text(), &min_cross_size); |
| + layout_->set_minimum_cross_axis_size(min_cross_size); |
| + } |
| + RefreshLayoutPanel(); |
| +} |
| + |
| +void BoxLayoutExample::RefreshLayoutPanel() { |
| + box_layout_panel_->Layout(); |
| + box_layout_panel_->SchedulePaint(); |
| +} |
| + |
| +void BoxLayoutExample::UpdateLayoutManager() { |
| + int child_spacing; |
| + int default_flex; |
| + int min_cross_size; |
| + base::StringToInt(between_child_spacing_->text(), &child_spacing); |
|
sky
2017/05/09 19:26:03
Remember that conversion may fail and return false
kylix_rd
2017/05/09 20:07:06
According to the doc, errors will set |*output| to
|
| + base::StringToInt(default_flex_->text(), &default_flex); |
| + base::StringToInt(min_cross_axis_size_->text(), &min_cross_size); |
| + layout_ = new BoxLayout(orientation_->selected_index() == 0 |
| + ? BoxLayout::Orientation::kHorizontal |
| + : BoxLayout::Orientation::kVertical, |
| + 0, 0, child_spacing, collapse_margins_->checked()); |
| + layout_->set_cross_axis_alignment(static_cast<BoxLayout::CrossAxisAlignment>( |
| + cross_axis_alignment_->selected_index())); |
| + layout_->set_main_axis_alignment(static_cast<BoxLayout::MainAxisAlignment>( |
| + main_axis_alignment_->selected_index())); |
| + layout_->SetDefaultFlex(default_flex); |
| + layout_->set_minimum_cross_axis_size(min_cross_size); |
| + for (int i = 0; i < box_layout_panel_->child_count(); ++i) { |
| + ChildPanel* panel = |
| + static_cast<ChildPanel*>(box_layout_panel_->child_at(i)); |
| + int flex = panel->GetFlex(); |
| + if (flex < 0) |
| + layout_->ClearFlexForView(panel); |
| + else |
| + layout_->SetFlexForView(panel, flex); |
| + } |
| + box_layout_panel_->SetLayoutManager(layout_); |
| +} |
| + |
| +} // namespace examples |
| +} // namespace views |