Add ICU message format support
[chromium-blink-merge.git] / ui / views / layout / box_layout.cc
blob37ed66504eda77c68ecebf1cc3e5f2987b6c9777
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 total_main_axis_size +=
65 MainAxisSizeForView(child, child_area.width()) + between_child_spacing_;
66 ++num_visible;
67 flex_sum += GetFlexForView(child);
70 if (!num_visible)
71 return;
73 total_main_axis_size -= between_child_spacing_;
74 // Free space can be negative indicating that the views want to overflow.
75 int main_free_space = MainAxisSize(child_area) - total_main_axis_size;
77 int position = MainAxisPosition(child_area);
78 int size = MainAxisSize(child_area);
79 if (!flex_sum) {
80 switch (main_axis_alignment_) {
81 case MAIN_AXIS_ALIGNMENT_START:
82 break;
83 case MAIN_AXIS_ALIGNMENT_CENTER:
84 position += main_free_space / 2;
85 size = total_main_axis_size;
86 break;
87 case MAIN_AXIS_ALIGNMENT_END:
88 position += main_free_space;
89 size = total_main_axis_size;
90 break;
91 default:
92 NOTREACHED();
93 break;
96 gfx::Rect new_child_area(child_area);
97 SetMainAxisPosition(position, &new_child_area);
98 SetMainAxisSize(size, &new_child_area);
99 child_area.Intersect(new_child_area);
102 int main_position = MainAxisPosition(child_area);
103 int total_padding = 0;
104 int current_flex = 0;
105 for (int i = 0; i < host->child_count(); ++i) {
106 View* child = host->child_at(i);
107 if (!child->visible())
108 continue;
110 // Calculate cross axis size.
111 gfx::Rect bounds(child_area);
112 SetMainAxisPosition(main_position, &bounds);
113 if (cross_axis_alignment_ != CROSS_AXIS_ALIGNMENT_STRETCH) {
114 int free_space = CrossAxisSize(bounds) - CrossAxisSizeForView(child);
115 int position = CrossAxisPosition(bounds);
116 if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_CENTER) {
117 position += free_space / 2;
118 } else if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_END) {
119 position += free_space;
121 SetCrossAxisPosition(position, &bounds);
122 SetCrossAxisSize(CrossAxisSizeForView(child), &bounds);
125 // Calculate flex padding.
126 int current_padding = 0;
127 if (GetFlexForView(child) > 0) {
128 current_flex += GetFlexForView(child);
129 int quot = (main_free_space * current_flex) / flex_sum;
130 int rem = (main_free_space * current_flex) % flex_sum;
131 current_padding = quot - total_padding;
132 // Use the current remainder to round to the nearest pixel.
133 if (std::abs(rem) * 2 >= flex_sum)
134 current_padding += main_free_space > 0 ? 1 : -1;
135 total_padding += current_padding;
138 // Set main axis size.
139 int child_main_axis_size = MainAxisSizeForView(child, child_area.width());
140 SetMainAxisSize(child_main_axis_size + current_padding, &bounds);
141 if (MainAxisSize(bounds) > 0 || GetFlexForView(child) > 0)
142 main_position += MainAxisSize(bounds) + between_child_spacing_;
144 // Clamp child view bounds to |child_area|.
145 bounds.Intersect(child_area);
146 child->SetBoundsRect(bounds);
149 // Flex views should have grown/shrunk to consume all free space.
150 if (flex_sum)
151 DCHECK_EQ(total_padding, main_free_space);
154 gfx::Size BoxLayout::GetPreferredSize(const View* host) const {
155 DCHECK_EQ(host_, host);
156 // Calculate the child views' preferred width.
157 int width = 0;
158 if (orientation_ == kVertical) {
159 for (int i = 0; i < host->child_count(); ++i) {
160 const View* child = host->child_at(i);
161 if (!child->visible())
162 continue;
164 width = std::max(width, child->GetPreferredSize().width());
166 width = std::max(width, minimum_cross_axis_size_);
169 return GetPreferredSizeForChildWidth(host, width);
172 int BoxLayout::GetPreferredHeightForWidth(const View* host, int width) const {
173 DCHECK_EQ(host_, host);
174 int child_width = width - NonChildSize(host).width();
175 return GetPreferredSizeForChildWidth(host, child_width).height();
178 void BoxLayout::Installed(View* host) {
179 DCHECK(!host_);
180 host_ = host;
183 void BoxLayout::Uninstalled(View* host) {
184 DCHECK_EQ(host_, host);
185 host_ = NULL;
186 flex_map_.clear();
189 void BoxLayout::ViewRemoved(View* host, View* view) {
190 ClearFlexForView(view);
193 int BoxLayout::GetFlexForView(const View* view) const {
194 std::map<const View*, int>::const_iterator it = flex_map_.find(view);
195 if (it == flex_map_.end())
196 return default_flex_;
198 return it->second;
201 int BoxLayout::MainAxisSize(const gfx::Rect& rect) const {
202 return orientation_ == kHorizontal ? rect.width() : rect.height();
205 int BoxLayout::MainAxisPosition(const gfx::Rect& rect) const {
206 return orientation_ == kHorizontal ? rect.x() : rect.y();
209 void BoxLayout::SetMainAxisSize(int size, gfx::Rect* rect) const {
210 if (orientation_ == kHorizontal)
211 rect->set_width(size);
212 else
213 rect->set_height(size);
216 void BoxLayout::SetMainAxisPosition(int position, gfx::Rect* rect) const {
217 if (orientation_ == kHorizontal)
218 rect->set_x(position);
219 else
220 rect->set_y(position);
223 int BoxLayout::CrossAxisSize(const gfx::Rect& rect) const {
224 return orientation_ == kVertical ? rect.width() : rect.height();
227 int BoxLayout::CrossAxisPosition(const gfx::Rect& rect) const {
228 return orientation_ == kVertical ? rect.x() : rect.y();
231 void BoxLayout::SetCrossAxisSize(int size, gfx::Rect* rect) const {
232 if (orientation_ == kVertical)
233 rect->set_width(size);
234 else
235 rect->set_height(size);
238 void BoxLayout::SetCrossAxisPosition(int position, gfx::Rect* rect) const {
239 if (orientation_ == kVertical)
240 rect->set_x(position);
241 else
242 rect->set_y(position);
245 int BoxLayout::MainAxisSizeForView(const View* view,
246 int child_area_width) const {
247 return orientation_ == kHorizontal
248 ? view->GetPreferredSize().width()
249 : view->GetHeightForWidth(cross_axis_alignment_ ==
250 CROSS_AXIS_ALIGNMENT_STRETCH
251 ? child_area_width
252 : view->GetPreferredSize().width());
255 int BoxLayout::CrossAxisSizeForView(const View* view) const {
256 return orientation_ == kVertical
257 ? view->GetPreferredSize().width()
258 : view->GetHeightForWidth(view->GetPreferredSize().width());
261 gfx::Size BoxLayout::GetPreferredSizeForChildWidth(const View* host,
262 int child_area_width) const {
263 gfx::Rect child_area_bounds;
265 if (orientation_ == kHorizontal) {
266 // Horizontal layouts ignore |child_area_width|, meaning they mimic the
267 // default behavior of GridLayout::GetPreferredHeightForWidth().
268 // TODO(estade): fix this if it ever becomes a problem.
269 int position = 0;
270 for (int i = 0; i < host->child_count(); ++i) {
271 const View* child = host->child_at(i);
272 if (!child->visible())
273 continue;
275 gfx::Size size(child->GetPreferredSize());
276 if (size.IsEmpty())
277 continue;
279 gfx::Rect child_bounds(position, 0, size.width(), size.height());
280 child_area_bounds.Union(child_bounds);
281 position += size.width() + between_child_spacing_;
283 child_area_bounds.set_height(
284 std::max(child_area_bounds.height(), minimum_cross_axis_size_));
285 } else {
286 int height = 0;
287 for (int i = 0; i < host->child_count(); ++i) {
288 const View* child = host->child_at(i);
289 if (!child->visible())
290 continue;
292 // Use the child area width for getting the height if the child is
293 // supposed to stretch. Use its preferred size otherwise.
294 int extra_height = MainAxisSizeForView(child, child_area_width);
295 // Only add |between_child_spacing_| if this is not the only child.
296 if (height != 0 && extra_height > 0)
297 height += between_child_spacing_;
298 height += extra_height;
301 child_area_bounds.set_width(child_area_width);
302 child_area_bounds.set_height(height);
305 gfx::Size non_child_size = NonChildSize(host);
306 return gfx::Size(child_area_bounds.width() + non_child_size.width(),
307 child_area_bounds.height() + non_child_size.height());
310 gfx::Size BoxLayout::NonChildSize(const View* host) const {
311 gfx::Insets insets(host->GetInsets());
312 return gfx::Size(insets.width() + inside_border_insets_.width(),
313 insets.height() + inside_border_insets_.height());
316 } // namespace views