summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcalamity@chromium.org <calamity@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-25 10:06:04 +0000
committercalamity@chromium.org <calamity@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-25 10:06:04 +0000
commit89628cd99442e57b64689af07b0a4cec34d95304 (patch)
tree0e6b5f66028476d96ffe4822fb3c1af8c8d279df
parent0af747d4923863ff6807d44320eafd39ea4a2a19 (diff)
downloadchromium_src-89628cd99442e57b64689af07b0a4cec34d95304.zip
chromium_src-89628cd99442e57b64689af07b0a4cec34d95304.tar.gz
chromium_src-89628cd99442e57b64689af07b0a4cec34d95304.tar.bz2
Add cross axis alignment for BoxLayout.
This CL adds a CrossAxisAlignment setting to BoxLayout which functions like the CSS align-items property. This property aligns the children items of a view in the non-orientation axis of the BoxLayout. BUG=386475 Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=279257 Review URL: https://codereview.chromium.org/333403005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@279656 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--ui/app_list/views/start_page_view.cc4
-rw-r--r--ui/views/layout/box_layout.cc103
-rw-r--r--ui/views/layout/box_layout.h47
-rw-r--r--ui/views/layout/box_layout_unittest.cc97
-rw-r--r--ui/views/test/test_views.cc8
-rw-r--r--ui/views/test/test_views.h7
6 files changed, 225 insertions, 41 deletions
diff --git a/ui/app_list/views/start_page_view.cc b/ui/app_list/views/start_page_view.cc
index 6aea513..c81fbb9 100644
--- a/ui/app_list/views/start_page_view.cc
+++ b/ui/app_list/views/start_page_view.cc
@@ -29,7 +29,7 @@ const int kTopMargin = 30;
const int kInstantContainerSpacing = 20;
// WebView constants.
-const int kWebViewWidth = 200;
+const int kWebViewWidth = 500;
const int kWebViewHeight = 105;
// DummySearchBoxView constants.
@@ -130,6 +130,8 @@ void StartPageView::InitInstantContainer() {
gfx::Insets(kTopMargin, 0, kInstantContainerSpacing, 0));
instant_layout_manager->set_main_axis_alignment(
views::BoxLayout::MAIN_AXIS_ALIGNMENT_END);
+ instant_layout_manager->set_cross_axis_alignment(
+ views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
instant_container_->SetLayoutManager(instant_layout_manager);
views::View* web_view = view_delegate_->CreateStartPageWebView(
diff --git a/ui/views/layout/box_layout.cc b/ui/views/layout/box_layout.cc
index c33264c..91504ad 100644
--- a/ui/views/layout/box_layout.cc
+++ b/ui/views/layout/box_layout.cc
@@ -19,7 +19,8 @@ BoxLayout::BoxLayout(BoxLayout::Orientation orientation,
inside_border_vertical_spacing,
inside_border_horizontal_spacing),
between_child_spacing_(between_child_spacing),
- main_axis_alignment_(MAIN_AXIS_ALIGNMENT_START) {
+ main_axis_alignment_(MAIN_AXIS_ALIGNMENT_START),
+ cross_axis_alignment_(CROSS_AXIS_ALIGNMENT_STRETCH) {
}
BoxLayout::~BoxLayout() {
@@ -38,13 +39,8 @@ void BoxLayout::Layout(View* host) {
View* child = host->child_at(i);
if (!child->visible())
continue;
- if (orientation_ == kHorizontal) {
- total_main_axis_size +=
- child->GetPreferredSize().width() + between_child_spacing_;
- } else {
- total_main_axis_size += child->GetHeightForWidth(child_area.width()) +
- between_child_spacing_;
- }
+ total_main_axis_size += MainAxisSizeForView(child, child_area.width()) +
+ between_child_spacing_;
++num_visible;
}
@@ -76,21 +72,28 @@ void BoxLayout::Layout(View* host) {
}
}
- int x = child_area.x();
- int y = child_area.y();
+ int main_position = MainAxisPosition(child_area);
for (int i = 0; i < host->child_count(); ++i) {
View* child = host->child_at(i);
if (child->visible()) {
- gfx::Rect bounds(x, y, child_area.width(), child_area.height());
- if (orientation_ == kHorizontal) {
- bounds.set_width(child->GetPreferredSize().width() + padding);
- if (bounds.width() > 0)
- x += bounds.width() + between_child_spacing_;
- } else {
- bounds.set_height(child->GetHeightForWidth(bounds.width()) + padding);
- if (bounds.height() > 0)
- y += bounds.height() + between_child_spacing_;
+ gfx::Rect bounds(child_area);
+ SetMainAxisPosition(main_position, &bounds);
+ if (cross_axis_alignment_ != CROSS_AXIS_ALIGNMENT_STRETCH) {
+ int free_space = CrossAxisSize(bounds) - CrossAxisSizeForView(child);
+ int position = CrossAxisPosition(bounds);
+ if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_CENTER) {
+ position += free_space / 2;
+ } else if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_END) {
+ position += free_space;
+ }
+ SetCrossAxisPosition(position, &bounds);
+ SetCrossAxisSize(CrossAxisSizeForView(child), &bounds);
}
+ int child_main_axis_size = MainAxisSizeForView(child, child_area.width());
+ SetMainAxisSize(child_main_axis_size + padding, &bounds);
+ if (MainAxisSize(bounds) > 0)
+ main_position += MainAxisSize(bounds) + between_child_spacing_;
+
// Clamp child view bounds to |child_area|.
bounds.Intersect(child_area);
child->SetBoundsRect(bounds);
@@ -119,26 +122,64 @@ int BoxLayout::GetPreferredHeightForWidth(const View* host, int width) const {
return GetPreferredSizeForChildWidth(host, child_width).height();
}
-int BoxLayout::MainAxisSize(const gfx::Rect& child_area) const {
- return orientation_ == kHorizontal ? child_area.width() : child_area.height();
+int BoxLayout::MainAxisSize(const gfx::Rect& rect) const {
+ return orientation_ == kHorizontal ? rect.width() : rect.height();
}
-int BoxLayout::MainAxisPosition(const gfx::Rect& child_area) const {
- return orientation_ == kHorizontal ? child_area.x() : child_area.y();
+int BoxLayout::MainAxisPosition(const gfx::Rect& rect) const {
+ return orientation_ == kHorizontal ? rect.x() : rect.y();
}
-void BoxLayout::SetMainAxisSize(int size, gfx::Rect* child_area) const {
+void BoxLayout::SetMainAxisSize(int size, gfx::Rect* rect) const {
if (orientation_ == kHorizontal)
- child_area->set_width(size);
+ rect->set_width(size);
else
- child_area->set_height(size);
+ rect->set_height(size);
}
-void BoxLayout::SetMainAxisPosition(int position, gfx::Rect* child_area) const {
+void BoxLayout::SetMainAxisPosition(int position, gfx::Rect* rect) const {
if (orientation_ == kHorizontal)
- child_area->set_x(position);
+ rect->set_x(position);
+ else
+ rect->set_y(position);
+}
+
+int BoxLayout::CrossAxisSize(const gfx::Rect& rect) const {
+ return orientation_ == kVertical ? rect.width() : rect.height();
+}
+
+int BoxLayout::CrossAxisPosition(const gfx::Rect& rect) const {
+ return orientation_ == kVertical ? rect.x() : rect.y();
+}
+
+void BoxLayout::SetCrossAxisSize(int size, gfx::Rect* rect) const {
+ if (orientation_ == kVertical)
+ rect->set_width(size);
+ else
+ rect->set_height(size);
+}
+
+void BoxLayout::SetCrossAxisPosition(int position, gfx::Rect* rect) const {
+ if (orientation_ == kVertical)
+ rect->set_x(position);
else
- child_area->set_y(position);
+ rect->set_y(position);
+}
+
+int BoxLayout::MainAxisSizeForView(const View* view,
+ int child_area_width) const {
+ return orientation_ == kHorizontal
+ ? view->GetPreferredSize().width()
+ : view->GetHeightForWidth(cross_axis_alignment_ ==
+ CROSS_AXIS_ALIGNMENT_STRETCH
+ ? child_area_width
+ : view->GetPreferredSize().width());
+}
+
+int BoxLayout::CrossAxisSizeForView(const View* view) const {
+ return orientation_ == kVertical
+ ? view->GetPreferredSize().width()
+ : view->GetHeightForWidth(view->GetPreferredSize().width());
}
gfx::Size BoxLayout::GetPreferredSizeForChildWidth(const View* host,
@@ -170,7 +211,9 @@ gfx::Size BoxLayout::GetPreferredSizeForChildWidth(const View* host,
if (!child->visible())
continue;
- int extra_height = child->GetHeightForWidth(child_area_width);
+ // Use the child area width for getting the height if the child is
+ // supposed to stretch. Use its preferred size otherwise.
+ int extra_height = MainAxisSizeForView(child, child_area_width);
// Only add |between_child_spacing_| if this is not the only child.
if (height != 0 && extra_height > 0)
height += between_child_spacing_;
diff --git a/ui/views/layout/box_layout.h b/ui/views/layout/box_layout.h
index e1d22ac..54fa502 100644
--- a/ui/views/layout/box_layout.h
+++ b/ui/views/layout/box_layout.h
@@ -47,8 +47,16 @@ class VIEWS_EXPORT BoxLayout : public LayoutManager {
// in-between the child views.
};
- // TODO(calamity): Add CrossAxisAlignment property to allow cross axis
- // alignment.
+ // This specifies where along the cross axis the children should be laid out.
+ // e.g. a horizontal layout of CROSS_AXIS_ALIGNMENT_END will result in the
+ // child views being bottom-aligned.
+ enum CrossAxisAlignment {
+ // This causes the child view to stretch to fit the host in the cross axis.
+ CROSS_AXIS_ALIGNMENT_STRETCH,
+ CROSS_AXIS_ALIGNMENT_START,
+ CROSS_AXIS_ALIGNMENT_CENTER,
+ CROSS_AXIS_ALIGNMENT_END,
+ };
// Use |inside_border_horizontal_spacing| and
// |inside_border_vertical_spacing| to add additional space between the child
@@ -64,6 +72,10 @@ class VIEWS_EXPORT BoxLayout : public LayoutManager {
main_axis_alignment_ = main_axis_alignment;
}
+ void set_cross_axis_alignment(CrossAxisAlignment cross_axis_alignment) {
+ cross_axis_alignment_ = cross_axis_alignment;
+ }
+
void set_inside_border_insets(const gfx::Insets& insets) {
inside_border_insets_ = insets;
}
@@ -75,13 +87,28 @@ class VIEWS_EXPORT BoxLayout : public LayoutManager {
int width) const OVERRIDE;
private:
- // Returns the size and position along the main axis of |child_area|.
- int MainAxisSize(const gfx::Rect& child_area) const;
- int MainAxisPosition(const gfx::Rect& child_area) const;
+ // Returns the size and position along the main axis of |rect|.
+ int MainAxisSize(const gfx::Rect& rect) const;
+ int MainAxisPosition(const gfx::Rect& rect) const;
+
+ // Sets the size and position along the main axis of |rect|.
+ void SetMainAxisSize(int size, gfx::Rect* rect) const;
+ void SetMainAxisPosition(int position, gfx::Rect* rect) const;
- // Sets the size and position along the main axis of |child_area|.
- void SetMainAxisSize(int size, gfx::Rect* child_area) const;
- void SetMainAxisPosition(int position, gfx::Rect* child_area) const;
+ // Returns the size and position along the cross axis of |rect|.
+ int CrossAxisSize(const gfx::Rect& rect) const;
+ int CrossAxisPosition(const gfx::Rect& rect) const;
+
+ // Sets the size and position along the cross axis of |rect|.
+ void SetCrossAxisSize(int size, gfx::Rect* rect) const;
+ void SetCrossAxisPosition(int size, gfx::Rect* rect) const;
+
+ // Returns the main axis size for the given view. |child_area_width| is needed
+ // to calculate the height of the view when the orientation is vertical.
+ int MainAxisSizeForView(const View* view, int child_area_width) const;
+
+ // Returns the cross axis size for the given view.
+ int CrossAxisSizeForView(const View* view) const;
// The preferred size for the dialog given the width of the child area.
gfx::Size GetPreferredSizeForChildWidth(const View* host,
@@ -103,6 +130,10 @@ class VIEWS_EXPORT BoxLayout : public LayoutManager {
// MAIN_AXIS_ALIGNMENT_START by default.
MainAxisAlignment main_axis_alignment_;
+ // The alignment of children in the cross axis. This is
+ // CROSS_AXIS_ALIGNMENT_STRETCH by default.
+ CrossAxisAlignment cross_axis_alignment_;
+
DISALLOW_IMPLICIT_CONSTRUCTORS(BoxLayout);
};
diff --git a/ui/views/layout/box_layout_unittest.cc b/ui/views/layout/box_layout_unittest.cc
index 42f8f42..5f58b15 100644
--- a/ui/views/layout/box_layout_unittest.cc
+++ b/ui/views/layout/box_layout_unittest.cc
@@ -162,7 +162,8 @@ TEST_F(BoxLayoutTest, UseHeightForWidth) {
layout_.reset(new BoxLayout(BoxLayout::kVertical, 0, 0, 0));
View* v1 = new StaticSizedView(gfx::Size(20, 10));
host_->AddChildView(v1);
- View* v2 = new ProportionallySizedView(2);
+ ProportionallySizedView* v2 = new ProportionallySizedView(2);
+ v2->set_preferred_width(10);
host_->AddChildView(v2);
EXPECT_EQ(gfx::Size(20, 50), layout_->GetPreferredSize(host_.get()));
@@ -172,6 +173,18 @@ TEST_F(BoxLayoutTest, UseHeightForWidth) {
EXPECT_EQ(gfx::Rect(0, 10, 20, 40), v2->bounds());
EXPECT_EQ(110, layout_->GetPreferredHeightForWidth(host_.get(), 50));
+
+ // Test without horizontal stretching of the views.
+ layout_->set_cross_axis_alignment(BoxLayout::CROSS_AXIS_ALIGNMENT_END);
+ EXPECT_EQ(gfx::Size(20, 30).ToString(),
+ layout_->GetPreferredSize(host_.get()).ToString());
+
+ host_->SetBounds(0, 0, 20, 30);
+ layout_->Layout(host_.get());
+ EXPECT_EQ(gfx::Rect(0, 0, 20, 10), v1->bounds());
+ EXPECT_EQ(gfx::Rect(10, 10, 10, 20), v2->bounds());
+
+ EXPECT_EQ(30, layout_->GetPreferredHeightForWidth(host_.get(), 50));
}
TEST_F(BoxLayoutTest, EmptyPreferredSize) {
@@ -266,4 +279,86 @@ TEST_F(BoxLayoutTest, MainAxisAlignmentVertical) {
EXPECT_EQ(gfx::Rect(10, 80, 20, 10).ToString(), v2->bounds().ToString());
}
+TEST_F(BoxLayoutTest, CrossAxisAlignmentHorizontal) {
+ layout_.reset(new BoxLayout(BoxLayout::kHorizontal, 10, 10, 10));
+
+ View* v1 = new StaticSizedView(gfx::Size(20, 20));
+ host_->AddChildView(v1);
+ View* v2 = new StaticSizedView(gfx::Size(10, 10));
+ host_->AddChildView(v2);
+
+ host_->SetBounds(0, 0, 100, 60);
+
+ // Stretch children to fill the available height by default.
+ layout_->Layout(host_.get());
+ EXPECT_EQ(gfx::Rect(10, 10, 20, 40).ToString(), v1->bounds().ToString());
+ EXPECT_EQ(gfx::Rect(40, 10, 10, 40).ToString(), v2->bounds().ToString());
+
+ // Ensure same results for CROSS_AXIS_ALIGNMENT_STRETCH.
+ layout_->set_cross_axis_alignment(BoxLayout::CROSS_AXIS_ALIGNMENT_STRETCH);
+ layout_->Layout(host_.get());
+ EXPECT_EQ(gfx::Rect(10, 10, 20, 40).ToString(), v1->bounds().ToString());
+ EXPECT_EQ(gfx::Rect(40, 10, 10, 40).ToString(), v2->bounds().ToString());
+
+ // Aligns children to the start vertically.
+ layout_->set_cross_axis_alignment(BoxLayout::CROSS_AXIS_ALIGNMENT_START);
+ layout_->Layout(host_.get());
+ EXPECT_EQ(gfx::Rect(10, 10, 20, 20).ToString(), v1->bounds().ToString());
+ EXPECT_EQ(gfx::Rect(40, 10, 10, 10).ToString(), v2->bounds().ToString());
+
+ // Aligns children to the center vertically.
+ layout_->set_cross_axis_alignment(BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
+ layout_->Layout(host_.get());
+ EXPECT_EQ(gfx::Rect(10, 20, 20, 20).ToString(), v1->bounds().ToString());
+ EXPECT_EQ(gfx::Rect(40, 25, 10, 10).ToString(), v2->bounds().ToString());
+
+ // Aligns children to the end of the host vertically, accounting for the
+ // inside border spacing.
+ layout_->set_cross_axis_alignment(BoxLayout::CROSS_AXIS_ALIGNMENT_END);
+ layout_->Layout(host_.get());
+ EXPECT_EQ(gfx::Rect(10, 30, 20, 20).ToString(), v1->bounds().ToString());
+ EXPECT_EQ(gfx::Rect(40, 40, 10, 10).ToString(), v2->bounds().ToString());
+}
+
+TEST_F(BoxLayoutTest, CrossAxisAlignmentVertical) {
+ layout_.reset(new BoxLayout(BoxLayout::kVertical, 10, 10, 10));
+
+ View* v1 = new StaticSizedView(gfx::Size(20, 20));
+ host_->AddChildView(v1);
+ View* v2 = new StaticSizedView(gfx::Size(10, 10));
+ host_->AddChildView(v2);
+
+ host_->SetBounds(0, 0, 60, 100);
+
+ // Stretch children to fill the available width by default.
+ layout_->Layout(host_.get());
+ EXPECT_EQ(gfx::Rect(10, 10, 40, 20).ToString(), v1->bounds().ToString());
+ EXPECT_EQ(gfx::Rect(10, 40, 40, 10).ToString(), v2->bounds().ToString());
+
+ // Ensure same results for CROSS_AXIS_ALIGNMENT_STRETCH.
+ layout_->set_cross_axis_alignment(BoxLayout::CROSS_AXIS_ALIGNMENT_STRETCH);
+ layout_->Layout(host_.get());
+ EXPECT_EQ(gfx::Rect(10, 10, 40, 20).ToString(), v1->bounds().ToString());
+ EXPECT_EQ(gfx::Rect(10, 40, 40, 10).ToString(), v2->bounds().ToString());
+
+ // Aligns children to the start horizontally.
+ layout_->set_cross_axis_alignment(BoxLayout::CROSS_AXIS_ALIGNMENT_START);
+ layout_->Layout(host_.get());
+ EXPECT_EQ(gfx::Rect(10, 10, 20, 20).ToString(), v1->bounds().ToString());
+ EXPECT_EQ(gfx::Rect(10, 40, 10, 10).ToString(), v2->bounds().ToString());
+
+ // Aligns children to the center horizontally.
+ layout_->set_cross_axis_alignment(BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
+ layout_->Layout(host_.get());
+ EXPECT_EQ(gfx::Rect(20, 10, 20, 20).ToString(), v1->bounds().ToString());
+ EXPECT_EQ(gfx::Rect(25, 40, 10, 10).ToString(), v2->bounds().ToString());
+
+ // Aligns children to the end of the host horizontally, accounting for the
+ // inside border spacing.
+ layout_->set_cross_axis_alignment(BoxLayout::CROSS_AXIS_ALIGNMENT_END);
+ layout_->Layout(host_.get());
+ EXPECT_EQ(gfx::Rect(30, 10, 20, 20).ToString(), v1->bounds().ToString());
+ EXPECT_EQ(gfx::Rect(40, 40, 10, 10).ToString(), v2->bounds().ToString());
+}
+
} // namespace views
diff --git a/ui/views/test/test_views.cc b/ui/views/test/test_views.cc
index 0c391b0..320ec8e 100644
--- a/ui/views/test/test_views.cc
+++ b/ui/views/test/test_views.cc
@@ -15,7 +15,7 @@ gfx::Size StaticSizedView::GetPreferredSize() const {
}
ProportionallySizedView::ProportionallySizedView(int factor)
- : factor_(factor) {}
+ : factor_(factor), preferred_width_(-1) {}
ProportionallySizedView::~ProportionallySizedView() {}
@@ -23,4 +23,10 @@ int ProportionallySizedView::GetHeightForWidth(int w) const {
return w * factor_;
}
+gfx::Size ProportionallySizedView::GetPreferredSize() const {
+ if (preferred_width_ >= 0)
+ return gfx::Size(preferred_width_, GetHeightForWidth(preferred_width_));
+ return View::GetPreferredSize();
+}
+
} // namespace views
diff --git a/ui/views/test/test_views.h b/ui/views/test/test_views.h
index 7d0cabf..be6f778 100644
--- a/ui/views/test/test_views.h
+++ b/ui/views/test/test_views.h
@@ -5,6 +5,7 @@
#ifndef UI_VIEWS_TEST_TEST_VIEWS_H_
#define UI_VIEWS_TEST_TEST_VIEWS_H_
+#include "base/memory/scoped_ptr.h"
#include "ui/views/view.h"
namespace views {
@@ -29,13 +30,19 @@ class ProportionallySizedView : public View {
explicit ProportionallySizedView(int factor);
virtual ~ProportionallySizedView();
+ void set_preferred_width(int width) { preferred_width_ = width; }
+
virtual int GetHeightForWidth(int w) const OVERRIDE;
+ virtual gfx::Size GetPreferredSize() const OVERRIDE;
private:
// The multiplicative factor between width and height, i.e.
// height = width * factor_.
int factor_;
+ // The width used as the preferred size. -1 if not used.
+ int preferred_width_;
+
DISALLOW_COPY_AND_ASSIGN(ProportionallySizedView);
};