Automated Commit: Committing new LKGM version 7479.0.0 for chromeos.
[chromium-blink-merge.git] / ui / views / layout / box_layout.cc
blob10f82b9aa544e349b18a852a4afe330de9176427
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/geometry/rect.h"
8 #include "ui/views/view.h"
10 namespace views {
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),
24 default_flex_(0),
25 minimum_cross_axis_size_(0),
26 host_(NULL) {
29 BoxLayout::~BoxLayout() {
32 void BoxLayout::SetFlexForView(const View* view, int flex_weight) {
33 DCHECK(host_);
34 DCHECK(view);
35 DCHECK_EQ(host_, view->parent());
36 DCHECK_GE(flex_weight, 0);
37 flex_map_[view] = flex_weight;
40 void BoxLayout::ClearFlexForView(const View* view) {
41 DCHECK(view);
42 flex_map_.erase(view);
45 void BoxLayout::SetDefaultFlex(int default_flex) {
46 DCHECK_GE(default_flex, 0);
47 default_flex_ = default_flex;
50 void BoxLayout::Layout(View* host) {
51 DCHECK_EQ(host_, host);
52 gfx::Rect child_area(host->GetLocalBounds());
53 child_area.Inset(host->GetInsets());
54 child_area.Inset(inside_border_insets_);
56 int total_main_axis_size = 0;
57 int num_visible = 0;
58 int flex_sum = 0;
59 // Calculate the total size of children in the main axis.
60 for (int i = 0; i < host->child_count(); ++i) {
61 View* child = host->child_at(i);
62 if (!child->visible())
63 continue;
64 int flex = GetFlexForView(child);
65 int child_main_axis_size = MainAxisSizeForView(child, child_area.width());
66 if (child_main_axis_size == 0 && flex == 0)
67 continue;
68 total_main_axis_size += child_main_axis_size + between_child_spacing_;
69 ++num_visible;
70 flex_sum += flex;
73 if (!num_visible)
74 return;
76 total_main_axis_size -= between_child_spacing_;
77 // Free space can be negative indicating that the views want to overflow.
78 int main_free_space = MainAxisSize(child_area) - total_main_axis_size;
80 int position = MainAxisPosition(child_area);
81 int size = MainAxisSize(child_area);
82 if (!flex_sum) {
83 switch (main_axis_alignment_) {
84 case MAIN_AXIS_ALIGNMENT_START:
85 break;
86 case MAIN_AXIS_ALIGNMENT_CENTER:
87 position += main_free_space / 2;
88 size = total_main_axis_size;
89 break;
90 case MAIN_AXIS_ALIGNMENT_END:
91 position += main_free_space;
92 size = total_main_axis_size;
93 break;
94 default:
95 NOTREACHED();
96 break;
99 gfx::Rect new_child_area(child_area);
100 SetMainAxisPosition(position, &new_child_area);
101 SetMainAxisSize(size, &new_child_area);
102 child_area.Intersect(new_child_area);
105 int main_position = MainAxisPosition(child_area);
106 int total_padding = 0;
107 int current_flex = 0;
108 for (int i = 0; i < host->child_count(); ++i) {
109 View* child = host->child_at(i);
110 if (!child->visible())
111 continue;
113 // Calculate cross axis size.
114 gfx::Rect bounds(child_area);
115 SetMainAxisPosition(main_position, &bounds);
116 if (cross_axis_alignment_ != CROSS_AXIS_ALIGNMENT_STRETCH) {
117 int free_space = CrossAxisSize(bounds) - CrossAxisSizeForView(child);
118 int position = CrossAxisPosition(bounds);
119 if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_CENTER) {
120 position += free_space / 2;
121 } else if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_END) {
122 position += free_space;
124 SetCrossAxisPosition(position, &bounds);
125 SetCrossAxisSize(CrossAxisSizeForView(child), &bounds);
128 // Calculate flex padding.
129 int current_padding = 0;
130 if (GetFlexForView(child) > 0) {
131 current_flex += GetFlexForView(child);
132 int quot = (main_free_space * current_flex) / flex_sum;
133 int rem = (main_free_space * current_flex) % flex_sum;
134 current_padding = quot - total_padding;
135 // Use the current remainder to round to the nearest pixel.
136 if (std::abs(rem) * 2 >= flex_sum)
137 current_padding += main_free_space > 0 ? 1 : -1;
138 total_padding += current_padding;
141 // Set main axis size.
142 int child_main_axis_size = MainAxisSizeForView(child, child_area.width());
143 SetMainAxisSize(child_main_axis_size + current_padding, &bounds);
144 if (MainAxisSize(bounds) > 0 || GetFlexForView(child) > 0)
145 main_position += MainAxisSize(bounds) + between_child_spacing_;
147 // Clamp child view bounds to |child_area|.
148 bounds.Intersect(child_area);
149 child->SetBoundsRect(bounds);
152 // Flex views should have grown/shrunk to consume all free space.
153 if (flex_sum)
154 DCHECK_EQ(total_padding, main_free_space);
157 gfx::Size BoxLayout::GetPreferredSize(const View* host) const {
158 DCHECK_EQ(host_, host);
159 // Calculate the child views' preferred width.
160 int width = 0;
161 if (orientation_ == kVertical) {
162 for (int i = 0; i < host->child_count(); ++i) {
163 const View* child = host->child_at(i);
164 if (!child->visible())
165 continue;
167 width = std::max(width, child->GetPreferredSize().width());
169 width = std::max(width, minimum_cross_axis_size_);
172 return GetPreferredSizeForChildWidth(host, width);
175 int BoxLayout::GetPreferredHeightForWidth(const View* host, int width) const {
176 DCHECK_EQ(host_, host);
177 int child_width = width - NonChildSize(host).width();
178 return GetPreferredSizeForChildWidth(host, child_width).height();
181 void BoxLayout::Installed(View* host) {
182 DCHECK(!host_);
183 host_ = host;
186 void BoxLayout::Uninstalled(View* host) {
187 DCHECK_EQ(host_, host);
188 host_ = NULL;
189 flex_map_.clear();
192 void BoxLayout::ViewRemoved(View* host, View* view) {
193 ClearFlexForView(view);
196 int BoxLayout::GetFlexForView(const View* view) const {
197 std::map<const View*, int>::const_iterator it = flex_map_.find(view);
198 if (it == flex_map_.end())
199 return default_flex_;
201 return it->second;
204 int BoxLayout::MainAxisSize(const gfx::Rect& rect) const {
205 return orientation_ == kHorizontal ? rect.width() : rect.height();
208 int BoxLayout::MainAxisPosition(const gfx::Rect& rect) const {
209 return orientation_ == kHorizontal ? rect.x() : rect.y();
212 void BoxLayout::SetMainAxisSize(int size, gfx::Rect* rect) const {
213 if (orientation_ == kHorizontal)
214 rect->set_width(size);
215 else
216 rect->set_height(size);
219 void BoxLayout::SetMainAxisPosition(int position, gfx::Rect* rect) const {
220 if (orientation_ == kHorizontal)
221 rect->set_x(position);
222 else
223 rect->set_y(position);
226 int BoxLayout::CrossAxisSize(const gfx::Rect& rect) const {
227 return orientation_ == kVertical ? rect.width() : rect.height();
230 int BoxLayout::CrossAxisPosition(const gfx::Rect& rect) const {
231 return orientation_ == kVertical ? rect.x() : rect.y();
234 void BoxLayout::SetCrossAxisSize(int size, gfx::Rect* rect) const {
235 if (orientation_ == kVertical)
236 rect->set_width(size);
237 else
238 rect->set_height(size);
241 void BoxLayout::SetCrossAxisPosition(int position, gfx::Rect* rect) const {
242 if (orientation_ == kVertical)
243 rect->set_x(position);
244 else
245 rect->set_y(position);
248 int BoxLayout::MainAxisSizeForView(const View* view,
249 int child_area_width) const {
250 return orientation_ == kHorizontal
251 ? view->GetPreferredSize().width()
252 : view->GetHeightForWidth(cross_axis_alignment_ ==
253 CROSS_AXIS_ALIGNMENT_STRETCH
254 ? child_area_width
255 : view->GetPreferredSize().width());
258 int BoxLayout::CrossAxisSizeForView(const View* view) const {
259 return orientation_ == kVertical
260 ? view->GetPreferredSize().width()
261 : view->GetHeightForWidth(view->GetPreferredSize().width());
264 gfx::Size BoxLayout::GetPreferredSizeForChildWidth(const View* host,
265 int child_area_width) const {
266 gfx::Rect child_area_bounds;
268 if (orientation_ == kHorizontal) {
269 // Horizontal layouts ignore |child_area_width|, meaning they mimic the
270 // default behavior of GridLayout::GetPreferredHeightForWidth().
271 // TODO(estade): fix this if it ever becomes a problem.
272 int position = 0;
273 for (int i = 0; i < host->child_count(); ++i) {
274 const View* child = host->child_at(i);
275 if (!child->visible())
276 continue;
278 gfx::Size size(child->GetPreferredSize());
279 if (size.IsEmpty())
280 continue;
282 gfx::Rect child_bounds(position, 0, size.width(), size.height());
283 child_area_bounds.Union(child_bounds);
284 position += size.width() + between_child_spacing_;
286 child_area_bounds.set_height(
287 std::max(child_area_bounds.height(), minimum_cross_axis_size_));
288 } else {
289 int height = 0;
290 for (int i = 0; i < host->child_count(); ++i) {
291 const View* child = host->child_at(i);
292 if (!child->visible())
293 continue;
295 // Use the child area width for getting the height if the child is
296 // supposed to stretch. Use its preferred size otherwise.
297 int extra_height = MainAxisSizeForView(child, child_area_width);
298 // Only add |between_child_spacing_| if this is not the only child.
299 if (height != 0 && extra_height > 0)
300 height += between_child_spacing_;
301 height += extra_height;
304 child_area_bounds.set_width(child_area_width);
305 child_area_bounds.set_height(height);
308 gfx::Size non_child_size = NonChildSize(host);
309 return gfx::Size(child_area_bounds.width() + non_child_size.width(),
310 child_area_bounds.height() + non_child_size.height());
313 gfx::Size BoxLayout::NonChildSize(const View* host) const {
314 gfx::Insets insets(host->GetInsets());
315 return gfx::Size(insets.width() + inside_border_insets_.width(),
316 insets.height() + inside_border_insets_.height());
319 } // namespace views