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
),
23 cross_axis_alignment_(CROSS_AXIS_ALIGNMENT_STRETCH
) {
26 BoxLayout::~BoxLayout() {
29 void BoxLayout::Layout(View
* host
) {
30 gfx::Rect
child_area(host
->GetLocalBounds());
31 child_area
.Inset(host
->GetInsets());
32 child_area
.Inset(inside_border_insets_
);
35 if (main_axis_alignment_
!= MAIN_AXIS_ALIGNMENT_START
) {
36 int total_main_axis_size
= 0;
38 for (int i
= 0; i
< host
->child_count(); ++i
) {
39 View
* child
= host
->child_at(i
);
40 if (!child
->visible())
42 total_main_axis_size
+= MainAxisSizeForView(child
, child_area
.width()) +
43 between_child_spacing_
;
48 total_main_axis_size
-= between_child_spacing_
;
49 int free_space
= MainAxisSize(child_area
) - total_main_axis_size
;
50 int position
= MainAxisPosition(child_area
);
51 int size
= MainAxisSize(child_area
);
52 switch (main_axis_alignment_
) {
53 case MAIN_AXIS_ALIGNMENT_FILL
:
54 padding
= std::max(free_space
/ num_visible
, 0);
56 case MAIN_AXIS_ALIGNMENT_CENTER
:
57 position
+= free_space
/ 2;
58 size
= total_main_axis_size
;
60 case MAIN_AXIS_ALIGNMENT_END
:
61 position
+= free_space
;
62 size
= total_main_axis_size
;
68 gfx::Rect
new_child_area(child_area
);
69 SetMainAxisPosition(position
, &new_child_area
);
70 SetMainAxisSize(size
, &new_child_area
);
71 child_area
.Intersect(new_child_area
);
75 int main_position
= MainAxisPosition(child_area
);
76 for (int i
= 0; i
< host
->child_count(); ++i
) {
77 View
* child
= host
->child_at(i
);
78 if (child
->visible()) {
79 gfx::Rect
bounds(child_area
);
80 SetMainAxisPosition(main_position
, &bounds
);
81 if (cross_axis_alignment_
!= CROSS_AXIS_ALIGNMENT_STRETCH
) {
82 int free_space
= CrossAxisSize(bounds
) - CrossAxisSizeForView(child
);
83 int position
= CrossAxisPosition(bounds
);
84 if (cross_axis_alignment_
== CROSS_AXIS_ALIGNMENT_CENTER
) {
85 position
+= free_space
/ 2;
86 } else if (cross_axis_alignment_
== CROSS_AXIS_ALIGNMENT_END
) {
87 position
+= free_space
;
89 SetCrossAxisPosition(position
, &bounds
);
90 SetCrossAxisSize(CrossAxisSizeForView(child
), &bounds
);
92 int child_main_axis_size
= MainAxisSizeForView(child
, child_area
.width());
93 SetMainAxisSize(child_main_axis_size
+ padding
, &bounds
);
94 if (MainAxisSize(bounds
) > 0)
95 main_position
+= MainAxisSize(bounds
) + between_child_spacing_
;
97 // Clamp child view bounds to |child_area|.
98 bounds
.Intersect(child_area
);
99 child
->SetBoundsRect(bounds
);
104 gfx::Size
BoxLayout::GetPreferredSize(const View
* host
) const {
105 // Calculate the child views' preferred width.
107 if (orientation_
== kVertical
) {
108 for (int i
= 0; i
< host
->child_count(); ++i
) {
109 const View
* child
= host
->child_at(i
);
110 if (!child
->visible())
113 width
= std::max(width
, child
->GetPreferredSize().width());
117 return GetPreferredSizeForChildWidth(host
, width
);
120 int BoxLayout::GetPreferredHeightForWidth(const View
* host
, int width
) const {
121 int child_width
= width
- NonChildSize(host
).width();
122 return GetPreferredSizeForChildWidth(host
, child_width
).height();
125 int BoxLayout::MainAxisSize(const gfx::Rect
& rect
) const {
126 return orientation_
== kHorizontal
? rect
.width() : rect
.height();
129 int BoxLayout::MainAxisPosition(const gfx::Rect
& rect
) const {
130 return orientation_
== kHorizontal
? rect
.x() : rect
.y();
133 void BoxLayout::SetMainAxisSize(int size
, gfx::Rect
* rect
) const {
134 if (orientation_
== kHorizontal
)
135 rect
->set_width(size
);
137 rect
->set_height(size
);
140 void BoxLayout::SetMainAxisPosition(int position
, gfx::Rect
* rect
) const {
141 if (orientation_
== kHorizontal
)
142 rect
->set_x(position
);
144 rect
->set_y(position
);
147 int BoxLayout::CrossAxisSize(const gfx::Rect
& rect
) const {
148 return orientation_
== kVertical
? rect
.width() : rect
.height();
151 int BoxLayout::CrossAxisPosition(const gfx::Rect
& rect
) const {
152 return orientation_
== kVertical
? rect
.x() : rect
.y();
155 void BoxLayout::SetCrossAxisSize(int size
, gfx::Rect
* rect
) const {
156 if (orientation_
== kVertical
)
157 rect
->set_width(size
);
159 rect
->set_height(size
);
162 void BoxLayout::SetCrossAxisPosition(int position
, gfx::Rect
* rect
) const {
163 if (orientation_
== kVertical
)
164 rect
->set_x(position
);
166 rect
->set_y(position
);
169 int BoxLayout::MainAxisSizeForView(const View
* view
,
170 int child_area_width
) const {
171 return orientation_
== kHorizontal
172 ? view
->GetPreferredSize().width()
173 : view
->GetHeightForWidth(cross_axis_alignment_
==
174 CROSS_AXIS_ALIGNMENT_STRETCH
176 : view
->GetPreferredSize().width());
179 int BoxLayout::CrossAxisSizeForView(const View
* view
) const {
180 return orientation_
== kVertical
181 ? view
->GetPreferredSize().width()
182 : view
->GetHeightForWidth(view
->GetPreferredSize().width());
185 gfx::Size
BoxLayout::GetPreferredSizeForChildWidth(const View
* host
,
186 int child_area_width
) const {
187 gfx::Rect child_area_bounds
;
189 if (orientation_
== kHorizontal
) {
190 // Horizontal layouts ignore |child_area_width|, meaning they mimic the
191 // default behavior of GridLayout::GetPreferredHeightForWidth().
192 // TODO(estade): fix this if it ever becomes a problem.
194 for (int i
= 0; i
< host
->child_count(); ++i
) {
195 const View
* child
= host
->child_at(i
);
196 if (!child
->visible())
199 gfx::Size
size(child
->GetPreferredSize());
203 gfx::Rect
child_bounds(position
, 0, size
.width(), size
.height());
204 child_area_bounds
.Union(child_bounds
);
205 position
+= size
.width() + between_child_spacing_
;
209 for (int i
= 0; i
< host
->child_count(); ++i
) {
210 const View
* child
= host
->child_at(i
);
211 if (!child
->visible())
214 // Use the child area width for getting the height if the child is
215 // supposed to stretch. Use its preferred size otherwise.
216 int extra_height
= MainAxisSizeForView(child
, child_area_width
);
217 // Only add |between_child_spacing_| if this is not the only child.
218 if (height
!= 0 && extra_height
> 0)
219 height
+= between_child_spacing_
;
220 height
+= extra_height
;
223 child_area_bounds
.set_width(child_area_width
);
224 child_area_bounds
.set_height(height
);
227 gfx::Size non_child_size
= NonChildSize(host
);
228 return gfx::Size(child_area_bounds
.width() + non_child_size
.width(),
229 child_area_bounds
.height() + non_child_size
.height());
232 gfx::Size
BoxLayout::NonChildSize(const View
* host
) const {
233 gfx::Insets
insets(host
->GetInsets());
234 return gfx::Size(insets
.width() + inside_border_insets_
.width(),
235 insets
.height() + inside_border_insets_
.height());