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.
4
5 #include "ui/views/layout/box_layout.h"
6
7 #include "ui/gfx/rect.h"
8 #include "ui/views/view.h"
9
10 namespace views {
11
BoxLayout(BoxLayout::Orientation orientation,int inside_border_horizontal_spacing,int inside_border_vertical_spacing,int between_child_spacing)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 }
24
~BoxLayout()25 BoxLayout::~BoxLayout() {
26 }
27
Layout(View * host)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_);
32
33 int padding = 0;
34 if (main_axis_alignment_ != MAIN_AXIS_ALIGNMENT_START) {
35 int total_main_axis_size = 0;
36 int num_visible = 0;
37 for (int i = 0; i < host->child_count(); ++i) {
38 View* child = host->child_at(i);
39 if (!child->visible())
40 continue;
41 if (orientation_ == kHorizontal) {
42 total_main_axis_size +=
43 child->GetPreferredSize().width() + between_child_spacing_;
44 } else {
45 total_main_axis_size += child->GetHeightForWidth(child_area.width()) +
46 between_child_spacing_;
47 }
48 ++num_visible;
49 }
50
51 if (num_visible) {
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);
59 break;
60 case MAIN_AXIS_ALIGNMENT_CENTER:
61 position += free_space / 2;
62 size = total_main_axis_size;
63 break;
64 case MAIN_AXIS_ALIGNMENT_END:
65 position += free_space;
66 size = total_main_axis_size;
67 break;
68 default:
69 NOTREACHED();
70 break;
71 }
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);
76 }
77 }
78
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_;
89 } else {
90 bounds.set_height(child->GetHeightForWidth(bounds.width()) + padding);
91 if (bounds.height() > 0)
92 y += bounds.height() + between_child_spacing_;
93 }
94 // Clamp child view bounds to |child_area|.
95 bounds.Intersect(child_area);
96 child->SetBoundsRect(bounds);
97 }
98 }
99 }
100
GetPreferredSize(const View * host) const101 gfx::Size BoxLayout::GetPreferredSize(const View* host) const {
102 // Calculate the child views' preferred width.
103 int width = 0;
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())
108 continue;
109
110 width = std::max(width, child->GetPreferredSize().width());
111 }
112 }
113
114 return GetPreferredSizeForChildWidth(host, width);
115 }
116
GetPreferredHeightForWidth(const View * host,int width) const117 int BoxLayout::GetPreferredHeightForWidth(const View* host, int width) const {
118 int child_width = width - NonChildSize(host).width();
119 return GetPreferredSizeForChildWidth(host, child_width).height();
120 }
121
MainAxisSize(const gfx::Rect & child_area) const122 int BoxLayout::MainAxisSize(const gfx::Rect& child_area) const {
123 return orientation_ == kHorizontal ? child_area.width() : child_area.height();
124 }
125
MainAxisPosition(const gfx::Rect & child_area) const126 int BoxLayout::MainAxisPosition(const gfx::Rect& child_area) const {
127 return orientation_ == kHorizontal ? child_area.x() : child_area.y();
128 }
129
SetMainAxisSize(int size,gfx::Rect * child_area) const130 void BoxLayout::SetMainAxisSize(int size, gfx::Rect* child_area) const {
131 if (orientation_ == kHorizontal)
132 child_area->set_width(size);
133 else
134 child_area->set_height(size);
135 }
136
SetMainAxisPosition(int position,gfx::Rect * child_area) const137 void BoxLayout::SetMainAxisPosition(int position, gfx::Rect* child_area) const {
138 if (orientation_ == kHorizontal)
139 child_area->set_x(position);
140 else
141 child_area->set_y(position);
142 }
143
GetPreferredSizeForChildWidth(const View * host,int child_area_width) const144 gfx::Size BoxLayout::GetPreferredSizeForChildWidth(const View* host,
145 int child_area_width) const {
146 gfx::Rect child_area_bounds;
147
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.
152 int position = 0;
153 for (int i = 0; i < host->child_count(); ++i) {
154 const View* child = host->child_at(i);
155 if (!child->visible())
156 continue;
157
158 gfx::Size size(child->GetPreferredSize());
159 if (size.IsEmpty())
160 continue;
161
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_;
165 }
166 } else {
167 int height = 0;
168 for (int i = 0; i < host->child_count(); ++i) {
169 const View* child = host->child_at(i);
170 if (!child->visible())
171 continue;
172
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;
178 }
179
180 child_area_bounds.set_width(child_area_width);
181 child_area_bounds.set_height(height);
182 }
183
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());
187 }
188
NonChildSize(const View * host) const189 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());
193 }
194
195 } // namespace views
196