1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/views/layout/box_layout.h"
7 #include "ui/gfx/rect.h"
8 #include "ui/views/view.h"
12 BoxLayout::BoxLayout(BoxLayout::Orientation orientation
,
13 int inside_border_horizontal_spacing
,
14 int inside_border_vertical_spacing
,
15 int between_child_spacing
)
16 : orientation_(orientation
),
17 inside_border_insets_(inside_border_vertical_spacing
,
18 inside_border_horizontal_spacing
,
19 inside_border_vertical_spacing
,
20 inside_border_horizontal_spacing
),
21 between_child_spacing_(between_child_spacing
),
22 main_axis_alignment_(MAIN_AXIS_ALIGNMENT_START
) {
25 BoxLayout::~BoxLayout() {
28 void BoxLayout::Layout(View
* host
) {
29 gfx::Rect
child_area(host
->GetLocalBounds());
30 child_area
.Inset(host
->GetInsets());
31 child_area
.Inset(inside_border_insets_
);
34 if (main_axis_alignment_
!= MAIN_AXIS_ALIGNMENT_START
) {
35 int total_main_axis_size
= 0;
37 for (int i
= 0; i
< host
->child_count(); ++i
) {
38 View
* child
= host
->child_at(i
);
39 if (!child
->visible())
41 if (orientation_
== kHorizontal
) {
42 total_main_axis_size
+=
43 child
->GetPreferredSize().width() + between_child_spacing_
;
45 total_main_axis_size
+= child
->GetHeightForWidth(child_area
.width()) +
46 between_child_spacing_
;
52 total_main_axis_size
-= between_child_spacing_
;
53 int free_space
= MainAxisSize(child_area
) - total_main_axis_size
;
54 int position
= MainAxisPosition(child_area
);
55 int size
= MainAxisSize(child_area
);
56 switch (main_axis_alignment_
) {
57 case MAIN_AXIS_ALIGNMENT_FILL
:
58 padding
= std::max(free_space
/ num_visible
, 0);
60 case MAIN_AXIS_ALIGNMENT_CENTER
:
61 position
+= free_space
/ 2;
62 size
= total_main_axis_size
;
64 case MAIN_AXIS_ALIGNMENT_END
:
65 position
+= free_space
;
66 size
= total_main_axis_size
;
72 gfx::Rect
new_child_area(child_area
);
73 SetMainAxisPosition(position
, &new_child_area
);
74 SetMainAxisSize(size
, &new_child_area
);
75 child_area
.Intersect(new_child_area
);
79 int x
= child_area
.x();
80 int y
= child_area
.y();
81 for (int i
= 0; i
< host
->child_count(); ++i
) {
82 View
* child
= host
->child_at(i
);
83 if (child
->visible()) {
84 gfx::Rect
bounds(x
, y
, child_area
.width(), child_area
.height());
85 if (orientation_
== kHorizontal
) {
86 bounds
.set_width(child
->GetPreferredSize().width() + padding
);
87 if (bounds
.width() > 0)
88 x
+= bounds
.width() + between_child_spacing_
;
90 bounds
.set_height(child
->GetHeightForWidth(bounds
.width()) + padding
);
91 if (bounds
.height() > 0)
92 y
+= bounds
.height() + between_child_spacing_
;
94 // Clamp child view bounds to |child_area|.
95 bounds
.Intersect(child_area
);
96 child
->SetBoundsRect(bounds
);
101 gfx::Size
BoxLayout::GetPreferredSize(const View
* host
) const {
102 // Calculate the child views' preferred width.
104 if (orientation_
== kVertical
) {
105 for (int i
= 0; i
< host
->child_count(); ++i
) {
106 const View
* child
= host
->child_at(i
);
107 if (!child
->visible())
110 width
= std::max(width
, child
->GetPreferredSize().width());
114 return GetPreferredSizeForChildWidth(host
, width
);
117 int BoxLayout::GetPreferredHeightForWidth(const View
* host
, int width
) const {
118 int child_width
= width
- NonChildSize(host
).width();
119 return GetPreferredSizeForChildWidth(host
, child_width
).height();
122 int BoxLayout::MainAxisSize(const gfx::Rect
& child_area
) const {
123 return orientation_
== kHorizontal
? child_area
.width() : child_area
.height();
126 int BoxLayout::MainAxisPosition(const gfx::Rect
& child_area
) const {
127 return orientation_
== kHorizontal
? child_area
.x() : child_area
.y();
130 void BoxLayout::SetMainAxisSize(int size
, gfx::Rect
* child_area
) const {
131 if (orientation_
== kHorizontal
)
132 child_area
->set_width(size
);
134 child_area
->set_height(size
);
137 void BoxLayout::SetMainAxisPosition(int position
, gfx::Rect
* child_area
) const {
138 if (orientation_
== kHorizontal
)
139 child_area
->set_x(position
);
141 child_area
->set_y(position
);
144 gfx::Size
BoxLayout::GetPreferredSizeForChildWidth(const View
* host
,
145 int child_area_width
) const {
146 gfx::Rect child_area_bounds
;
148 if (orientation_
== kHorizontal
) {
149 // Horizontal layouts ignore |child_area_width|, meaning they mimic the
150 // default behavior of GridLayout::GetPreferredHeightForWidth().
151 // TODO(estade): fix this if it ever becomes a problem.
153 for (int i
= 0; i
< host
->child_count(); ++i
) {
154 const View
* child
= host
->child_at(i
);
155 if (!child
->visible())
158 gfx::Size
size(child
->GetPreferredSize());
162 gfx::Rect
child_bounds(position
, 0, size
.width(), size
.height());
163 child_area_bounds
.Union(child_bounds
);
164 position
+= size
.width() + between_child_spacing_
;
168 for (int i
= 0; i
< host
->child_count(); ++i
) {
169 const View
* child
= host
->child_at(i
);
170 if (!child
->visible())
173 int extra_height
= child
->GetHeightForWidth(child_area_width
);
174 // Only add |between_child_spacing_| if this is not the only child.
175 if (height
!= 0 && extra_height
> 0)
176 height
+= between_child_spacing_
;
177 height
+= extra_height
;
180 child_area_bounds
.set_width(child_area_width
);
181 child_area_bounds
.set_height(height
);
184 gfx::Size non_child_size
= NonChildSize(host
);
185 return gfx::Size(child_area_bounds
.width() + non_child_size
.width(),
186 child_area_bounds
.height() + non_child_size
.height());
189 gfx::Size
BoxLayout::NonChildSize(const View
* host
) const {
190 gfx::Insets
insets(host
->GetInsets());
191 return gfx::Size(insets
.width() + inside_border_insets_
.width(),
192 insets
.height() + inside_border_insets_
.height());