Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1405)

Side by Side Diff: ui/views/layout/box_layout.cc

Issue 2836313002: BoxLayout now suports per-view margins. (Closed)
Patch Set: Merged with master. Removed cached orientation_ from ViewWrapper. Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ui/views/layout/box_layout.h ('k') | ui/views/layout/box_layout_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/views/layout/box_layout.h" 5 #include "ui/views/layout/box_layout.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "ui/gfx/geometry/rect.h" 9 #include "ui/gfx/geometry/rect.h"
10 #include "ui/views/view.h" 10 #include "ui/views/view.h"
11 #include "ui/views/view_properties.h"
11 12
12 namespace views { 13 namespace views {
13 14
15 namespace {
16
17 // Returns the maximum of the given insets along the given |axis|.
18 // NOTE: |axis| is different from |orientation_|; it specifies the actual
19 // desired axis.
20 enum Axis { HORIZONTAL_AXIS, VERTICAL_AXIS };
21
22 gfx::Insets MaxAxisInsets(Axis axis,
23 const gfx::Insets& leading1,
24 const gfx::Insets& leading2,
25 const gfx::Insets& trailing1,
26 const gfx::Insets& trailing2) {
27 if (axis == HORIZONTAL_AXIS) {
28 return gfx::Insets(0, std::max(leading1.left(), leading2.left()), 0,
29 std::max(trailing1.right(), trailing2.right()));
30 }
31 return gfx::Insets(std::max(leading1.top(), leading2.top()), 0,
32 std::max(trailing1.bottom(), trailing2.bottom()), 0);
33 }
34
35 } // namespace
36
37 BoxLayout::ViewWrapper::ViewWrapper() : view_(nullptr), layout_(nullptr) {}
38
39 BoxLayout::ViewWrapper::ViewWrapper(const BoxLayout* layout, View* view)
40 : view_(view), layout_(layout) {
41 gfx::Insets* margins = view_ ? view_->GetProperty(kMarginsKey) : nullptr;
42 if (margins)
43 margins_ = *margins;
44 }
45
46 BoxLayout::ViewWrapper::~ViewWrapper() {}
47
48 int BoxLayout::ViewWrapper::GetHeightForWidth(int width) const {
49 // When collapse_margins_spacing_ is true, the BoxLayout handles the margin
50 // calculations because it has to compare and use only the largest of several
51 // adjacent margins or border insets.
52 if (layout_->collapse_margins_spacing_)
53 return view_->GetHeightForWidth(width);
54 // When collapse_margins_spacing_ is false, the view margins are included in
55 // the "virtual" size of the view. The view itself is unaware of this, so this
56 // information has to be excluded before the call to View::GetHeightForWidth()
57 // and added back in to the result.
58 // If the orientation_ is kVertical, the cross-axis is the actual view width.
59 // This is because the cross-axis margins are always handled by the layout.
60 if (layout_->orientation_ == Orientation::kHorizontal) {
61 return view_->GetHeightForWidth(std::max(0, width - margins_.width())) +
62 margins_.height();
63 }
64 return view_->GetHeightForWidth(width) + margins_.height();
65 }
66
67 gfx::Size BoxLayout::ViewWrapper::GetPreferredSize() const {
68 gfx::Size preferred_size = view_->GetPreferredSize();
69 if (!layout_->collapse_margins_spacing_)
70 preferred_size.Enlarge(margins_.width(), margins_.height());
71 return preferred_size;
72 }
73
74 void BoxLayout::ViewWrapper::SetBoundsRect(const gfx::Rect& bounds) {
75 gfx::Rect new_bounds = bounds;
76 if (!layout_->collapse_margins_spacing_) {
77 if (layout_->orientation_ == Orientation::kHorizontal) {
78 new_bounds.set_x(bounds.x() + margins_.left());
79 new_bounds.set_width(std::max(0, bounds.width() - margins_.width()));
80 } else {
81 new_bounds.set_y(bounds.y() + margins_.top());
82 new_bounds.set_height(std::max(0, bounds.height() - margins_.height()));
83 }
84 }
85 view_->SetBoundsRect(new_bounds);
86 }
87
88 bool BoxLayout::ViewWrapper::visible() const {
89 return view_->visible();
90 }
91
14 BoxLayout::BoxLayout(BoxLayout::Orientation orientation, 92 BoxLayout::BoxLayout(BoxLayout::Orientation orientation,
15 const gfx::Insets& inside_border_insets, 93 const gfx::Insets& inside_border_insets,
16 int between_child_spacing) 94 int between_child_spacing,
95 bool collapse_margins_spacing)
17 : orientation_(orientation), 96 : orientation_(orientation),
18 inside_border_insets_(inside_border_insets), 97 inside_border_insets_(inside_border_insets),
19 between_child_spacing_(between_child_spacing), 98 between_child_spacing_(between_child_spacing),
20 main_axis_alignment_(MAIN_AXIS_ALIGNMENT_START), 99 main_axis_alignment_(MAIN_AXIS_ALIGNMENT_START),
21 cross_axis_alignment_(CROSS_AXIS_ALIGNMENT_STRETCH), 100 cross_axis_alignment_(CROSS_AXIS_ALIGNMENT_STRETCH),
22 default_flex_(0), 101 default_flex_(0),
23 minimum_cross_axis_size_(0), 102 minimum_cross_axis_size_(0),
103 collapse_margins_spacing_(collapse_margins_spacing),
24 host_(nullptr) {} 104 host_(nullptr) {}
25 105
26 BoxLayout::~BoxLayout() { 106 BoxLayout::~BoxLayout() {
27 } 107 }
28 108
29 void BoxLayout::SetFlexForView(const View* view, int flex_weight) { 109 void BoxLayout::SetFlexForView(const View* view, int flex_weight) {
30 DCHECK(host_); 110 DCHECK(host_);
31 DCHECK(view); 111 DCHECK(view);
32 DCHECK_EQ(host_, view->parent()); 112 DCHECK_EQ(host_, view->parent());
33 DCHECK_GE(flex_weight, 0); 113 DCHECK_GE(flex_weight, 0);
34 flex_map_[view] = flex_weight; 114 flex_map_[view] = flex_weight;
35 } 115 }
36 116
37 void BoxLayout::ClearFlexForView(const View* view) { 117 void BoxLayout::ClearFlexForView(const View* view) {
38 DCHECK(view); 118 DCHECK(view);
39 flex_map_.erase(view); 119 flex_map_.erase(view);
40 } 120 }
41 121
42 void BoxLayout::SetDefaultFlex(int default_flex) { 122 void BoxLayout::SetDefaultFlex(int default_flex) {
43 DCHECK_GE(default_flex, 0); 123 DCHECK_GE(default_flex, 0);
44 default_flex_ = default_flex; 124 default_flex_ = default_flex;
45 } 125 }
46 126
47 void BoxLayout::Layout(View* host) { 127 void BoxLayout::Layout(View* host) {
48 DCHECK_EQ(host_, host); 128 DCHECK_EQ(host_, host);
49 gfx::Rect child_area(host->GetLocalBounds()); 129 gfx::Rect child_area(host->GetContentsBounds());
50 child_area.Inset(host->GetInsets()); 130
51 child_area.Inset(inside_border_insets_); 131 AdjustMainAxisForMargin(&child_area);
132 gfx::Insets max_cross_axis_margin;
133 if (!collapse_margins_spacing_) {
134 AdjustCrossAxisForInsets(&child_area);
135 max_cross_axis_margin = CrossAxisMaxViewMargin();
136 }
137 if (child_area.IsEmpty())
138 return;
52 139
53 int total_main_axis_size = 0; 140 int total_main_axis_size = 0;
54 int num_visible = 0; 141 int num_visible = 0;
55 int flex_sum = 0; 142 int flex_sum = 0;
56 // Calculate the total size of children in the main axis. 143 // Calculate the total size of children in the main axis.
57 for (int i = 0; i < host->child_count(); ++i) { 144 for (int i = 0; i < host->child_count(); ++i) {
58 View* child = host->child_at(i); 145 const ViewWrapper child(this, host->child_at(i));
59 if (!child->visible()) 146 if (!child.visible())
60 continue; 147 continue;
61 int flex = GetFlexForView(child); 148 int flex = GetFlexForView(child.view());
62 int child_main_axis_size = MainAxisSizeForView(child, child_area.width()); 149 int child_main_axis_size = MainAxisSizeForView(child, child_area.width());
63 if (child_main_axis_size == 0 && flex == 0) 150 if (child_main_axis_size == 0 && flex == 0)
64 continue; 151 continue;
65 total_main_axis_size += child_main_axis_size + between_child_spacing_; 152 total_main_axis_size += child_main_axis_size +
153 MainAxisMarginBetweenViews(
154 child, ViewWrapper(this, NextVisibleView(i)));
66 ++num_visible; 155 ++num_visible;
67 flex_sum += flex; 156 flex_sum += flex;
68 } 157 }
69 158
70 if (!num_visible) 159 if (!num_visible)
71 return; 160 return;
72 161
73 total_main_axis_size -= between_child_spacing_; 162 total_main_axis_size -= between_child_spacing_;
74 // Free space can be negative indicating that the views want to overflow. 163 // Free space can be negative indicating that the views want to overflow.
75 int main_free_space = MainAxisSize(child_area) - total_main_axis_size; 164 int main_free_space = MainAxisSize(child_area) - total_main_axis_size;
(...skipping 20 matching lines...) Expand all
96 gfx::Rect new_child_area(child_area); 185 gfx::Rect new_child_area(child_area);
97 SetMainAxisPosition(position, &new_child_area); 186 SetMainAxisPosition(position, &new_child_area);
98 SetMainAxisSize(size, &new_child_area); 187 SetMainAxisSize(size, &new_child_area);
99 child_area.Intersect(new_child_area); 188 child_area.Intersect(new_child_area);
100 } 189 }
101 190
102 int main_position = MainAxisPosition(child_area); 191 int main_position = MainAxisPosition(child_area);
103 int total_padding = 0; 192 int total_padding = 0;
104 int current_flex = 0; 193 int current_flex = 0;
105 for (int i = 0; i < host->child_count(); ++i) { 194 for (int i = 0; i < host->child_count(); ++i) {
106 View* child = host->child_at(i); 195 ViewWrapper child(this, host->child_at(i));
107 if (!child->visible()) 196 if (!child.visible())
108 continue; 197 continue;
109 198
110 // TODO(bruthig): Fix this. The main axis should be calculated before 199 // TODO(bruthig): Fix this. The main axis should be calculated before
111 // the cross axis size because child Views may calculate their cross axis 200 // the cross axis size because child Views may calculate their cross axis
112 // size based on their main axis size. See https://crbug.com/682266. 201 // size based on their main axis size. See https://crbug.com/682266.
113 202
114 // Calculate cross axis size. 203 // Calculate cross axis size.
115 gfx::Rect bounds(child_area); 204 gfx::Rect bounds(child_area);
205 gfx::Rect min_child_area(child_area);
206 gfx::Insets child_margins;
207 if (collapse_margins_spacing_) {
208 child_margins = MaxAxisInsets(
209 orientation_ == kVertical ? HORIZONTAL_AXIS : VERTICAL_AXIS,
210 child.margins(), inside_border_insets_, child.margins(),
211 inside_border_insets_);
212 } else {
213 child_margins = child.margins();
214 }
215
216 if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_STRETCH ||
217 cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_CENTER) {
218 InsetCrossAxis(&min_child_area, CrossAxisLeadingInset(child_margins),
219 CrossAxisTrailingInset(child_margins));
220 }
221
116 SetMainAxisPosition(main_position, &bounds); 222 SetMainAxisPosition(main_position, &bounds);
117 if (cross_axis_alignment_ != CROSS_AXIS_ALIGNMENT_STRETCH) { 223 if (cross_axis_alignment_ != CROSS_AXIS_ALIGNMENT_STRETCH) {
118 int free_space = CrossAxisSize(bounds) - CrossAxisSizeForView(child); 224 int cross_axis_margin_size = CrossAxisMarginSizeForView(child);
225 int view_cross_axis_size =
226 CrossAxisSizeForView(child) - cross_axis_margin_size;
227 int free_space = CrossAxisSize(bounds) - view_cross_axis_size;
119 int position = CrossAxisPosition(bounds); 228 int position = CrossAxisPosition(bounds);
120 if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_CENTER) { 229 if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_CENTER) {
230 if (view_cross_axis_size > CrossAxisSize(min_child_area))
231 view_cross_axis_size = CrossAxisSize(min_child_area);
121 position += free_space / 2; 232 position += free_space / 2;
233 position = std::max(position, CrossAxisLeadingEdge(min_child_area));
122 } else if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_END) { 234 } else if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_END) {
123 position += free_space; 235 position += free_space - CrossAxisTrailingInset(max_cross_axis_margin);
236 if (!collapse_margins_spacing_)
237 InsetCrossAxis(&min_child_area,
238 CrossAxisLeadingInset(child.margins()),
239 CrossAxisTrailingInset(max_cross_axis_margin));
240 } else {
241 position += CrossAxisLeadingInset(max_cross_axis_margin);
242 if (!collapse_margins_spacing_)
243 InsetCrossAxis(&min_child_area,
244 CrossAxisLeadingInset(max_cross_axis_margin),
245 CrossAxisTrailingInset(child.margins()));
124 } 246 }
125 SetCrossAxisPosition(position, &bounds); 247 SetCrossAxisPosition(position, &bounds);
126 SetCrossAxisSize(CrossAxisSizeForView(child), &bounds); 248 SetCrossAxisSize(view_cross_axis_size, &bounds);
127 } 249 }
128 250
129 // Calculate flex padding. 251 // Calculate flex padding.
130 int current_padding = 0; 252 int current_padding = 0;
131 if (GetFlexForView(child) > 0) { 253 if (GetFlexForView(child.view()) > 0) {
132 current_flex += GetFlexForView(child); 254 current_flex += GetFlexForView(child.view());
133 int quot = (main_free_space * current_flex) / flex_sum; 255 int quot = (main_free_space * current_flex) / flex_sum;
134 int rem = (main_free_space * current_flex) % flex_sum; 256 int rem = (main_free_space * current_flex) % flex_sum;
135 current_padding = quot - total_padding; 257 current_padding = quot - total_padding;
136 // Use the current remainder to round to the nearest pixel. 258 // Use the current remainder to round to the nearest pixel.
137 if (std::abs(rem) * 2 >= flex_sum) 259 if (std::abs(rem) * 2 >= flex_sum)
138 current_padding += main_free_space > 0 ? 1 : -1; 260 current_padding += main_free_space > 0 ? 1 : -1;
139 total_padding += current_padding; 261 total_padding += current_padding;
140 } 262 }
141 263
142 // Set main axis size. 264 // Set main axis size.
143 // TODO(bruthig): Use the allocated width to determine the cross axis size. 265 // TODO(bruthig): Use the allocated width to determine the cross axis size.
144 // See https://crbug.com/682266. 266 // See https://crbug.com/682266.
145 int child_main_axis_size = MainAxisSizeForView(child, child_area.width()); 267 int child_main_axis_size = MainAxisSizeForView(child, child_area.width());
146 SetMainAxisSize(child_main_axis_size + current_padding, &bounds); 268 SetMainAxisSize(child_main_axis_size + current_padding, &bounds);
147 if (MainAxisSize(bounds) > 0 || GetFlexForView(child) > 0) 269 if (MainAxisSize(bounds) > 0 || GetFlexForView(child.view()) > 0)
148 main_position += MainAxisSize(bounds) + between_child_spacing_; 270 main_position += MainAxisSize(bounds) +
271 MainAxisMarginBetweenViews(
272 child, ViewWrapper(this, NextVisibleView(i)));
149 273
150 // Clamp child view bounds to |child_area|. 274 // Clamp child view bounds to |child_area|.
151 bounds.Intersect(child_area); 275 bounds.Intersect(min_child_area);
152 child->SetBoundsRect(bounds); 276 child.SetBoundsRect(bounds);
153 } 277 }
154 278
155 // Flex views should have grown/shrunk to consume all free space. 279 // Flex views should have grown/shrunk to consume all free space.
156 if (flex_sum) 280 if (flex_sum)
157 DCHECK_EQ(total_padding, main_free_space); 281 DCHECK_EQ(total_padding, main_free_space);
158 } 282 }
159 283
160 gfx::Size BoxLayout::GetPreferredSize(const View* host) const { 284 gfx::Size BoxLayout::GetPreferredSize(const View* host) const {
161 DCHECK_EQ(host_, host); 285 DCHECK_EQ(host_, host);
162 // Calculate the child views' preferred width. 286 // Calculate the child views' preferred width.
163 int width = 0; 287 int width = 0;
164 if (orientation_ == kVertical) { 288 if (orientation_ == kVertical) {
165 for (int i = 0; i < host->child_count(); ++i) { 289 // Calculating the child views' overall preferred width is a little involved
166 const View* child = host->child_at(i); 290 // because of the way the margins interact with |cross_axis_alignment_|.
167 if (!child->visible()) 291 int leading = 0;
292 int trailing = 0;
293 gfx::Rect child_view_area;
294 for (int i = 0; i < host_->child_count(); ++i) {
295 const ViewWrapper child(this, host_->child_at(i));
296 if (!child.visible())
168 continue; 297 continue;
169 298
170 width = std::max(width, child->GetPreferredSize().width()); 299 // We need to bypass the ViewWrapper GetPreferredSize() to get the actual
300 // raw view size because the margins along the cross axis are handled
301 // below.
302 gfx::Size child_size = child.view()->GetPreferredSize();
303 gfx::Insets child_margins;
304 if (collapse_margins_spacing_) {
305 child_margins = MaxAxisInsets(HORIZONTAL_AXIS, child.margins(),
306 inside_border_insets_, child.margins(),
307 inside_border_insets_);
308 } else {
309 child_margins = child.margins();
310 }
311
312 // The value of |cross_axis_alignment_| will determine how the view's
313 // margins interact with each other or the |inside_border_insets_|.
314 if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_START) {
315 leading = std::max(leading, CrossAxisLeadingInset(child_margins));
316 width = std::max(
317 width, child_size.width() + CrossAxisTrailingInset(child_margins));
318 } else if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_END) {
319 trailing = std::max(trailing, CrossAxisTrailingInset(child_margins));
320 width = std::max(
321 width, child_size.width() + CrossAxisLeadingInset(child_margins));
322 } else {
323 // We don't have a rectangle which can be used to calculate a common
324 // center-point, so a single known point (0) along the horizontal axis
325 // is used. This is OK because we're only interested in the overall
326 // width and not the position.
327 gfx::Rect child_bounds =
328 gfx::Rect(-(child_size.width() / 2), 0, child_size.width(),
329 child_size.height());
330 child_bounds.Inset(-child.margins().left(), 0, -child.margins().right(),
331 0);
332 child_view_area.Union(child_bounds);
333 width = std::max(width, child_view_area.width());
334 }
171 } 335 }
172 width = std::max(width, minimum_cross_axis_size_); 336 width = std::max(width + leading + trailing, minimum_cross_axis_size_);
173 } 337 }
174 338
175 return GetPreferredSizeForChildWidth(host, width); 339 return GetPreferredSizeForChildWidth(host, width);
176 } 340 }
177 341
178 int BoxLayout::GetPreferredHeightForWidth(const View* host, int width) const { 342 int BoxLayout::GetPreferredHeightForWidth(const View* host, int width) const {
179 DCHECK_EQ(host_, host); 343 DCHECK_EQ(host_, host);
180 int child_width = width - NonChildSize(host).width(); 344 int child_width = width - NonChildSize(host).width();
181 return GetPreferredSizeForChildWidth(host, child_width).height(); 345 return GetPreferredSizeForChildWidth(host, child_width).height();
182 } 346 }
183 347
184 void BoxLayout::Installed(View* host) { 348 void BoxLayout::Installed(View* host) {
185 DCHECK(!host_); 349 DCHECK(!host_);
186 host_ = host; 350 host_ = host;
187 } 351 }
188 352
189 void BoxLayout::ViewRemoved(View* host, View* view) { 353 void BoxLayout::ViewRemoved(View* host, View* view) {
190 ClearFlexForView(view); 354 ClearFlexForView(view);
191 } 355 }
192 356
193 int BoxLayout::GetFlexForView(const View* view) const { 357 int BoxLayout::GetFlexForView(const View* view) const {
194 std::map<const View*, int>::const_iterator it = flex_map_.find(view); 358 FlexMap::const_iterator it = flex_map_.find(view);
195 if (it == flex_map_.end()) 359 if (it == flex_map_.end())
196 return default_flex_; 360 return default_flex_;
197 361
198 return it->second; 362 return it->second;
199 } 363 }
200 364
201 int BoxLayout::MainAxisSize(const gfx::Rect& rect) const { 365 int BoxLayout::MainAxisSize(const gfx::Rect& rect) const {
202 return orientation_ == kHorizontal ? rect.width() : rect.height(); 366 return orientation_ == kHorizontal ? rect.width() : rect.height();
203 } 367 }
204 368
(...skipping 30 matching lines...) Expand all
235 rect->set_height(size); 399 rect->set_height(size);
236 } 400 }
237 401
238 void BoxLayout::SetCrossAxisPosition(int position, gfx::Rect* rect) const { 402 void BoxLayout::SetCrossAxisPosition(int position, gfx::Rect* rect) const {
239 if (orientation_ == kVertical) 403 if (orientation_ == kVertical)
240 rect->set_x(position); 404 rect->set_x(position);
241 else 405 else
242 rect->set_y(position); 406 rect->set_y(position);
243 } 407 }
244 408
245 int BoxLayout::MainAxisSizeForView(const View* view, 409 int BoxLayout::MainAxisSizeForView(const ViewWrapper& view,
246 int child_area_width) const { 410 int child_area_width) const {
247 return orientation_ == kHorizontal 411 return orientation_ == kHorizontal
248 ? view->GetPreferredSize().width() 412 ? view.GetPreferredSize().width()
249 : view->GetHeightForWidth(cross_axis_alignment_ == 413 : view.GetHeightForWidth(cross_axis_alignment_ ==
250 CROSS_AXIS_ALIGNMENT_STRETCH 414 CROSS_AXIS_ALIGNMENT_STRETCH
251 ? child_area_width 415 ? child_area_width
252 : view->GetPreferredSize().width()); 416 : view.GetPreferredSize().width());
253 } 417 }
254 418
255 int BoxLayout::CrossAxisSizeForView(const View* view) const { 419 int BoxLayout::MainAxisLeadingInset(const gfx::Insets& insets) const {
420 return orientation_ == kHorizontal ? insets.left() : insets.top();
421 }
422
423 int BoxLayout::MainAxisTrailingInset(const gfx::Insets& insets) const {
424 return orientation_ == kHorizontal ? insets.right() : insets.bottom();
425 }
426
427 int BoxLayout::CrossAxisLeadingEdge(const gfx::Rect& rect) const {
428 return orientation_ == kVertical ? rect.x() : rect.y();
429 }
430
431 int BoxLayout::CrossAxisLeadingInset(const gfx::Insets& insets) const {
432 return orientation_ == kVertical ? insets.left() : insets.top();
433 }
434
435 int BoxLayout::CrossAxisTrailingInset(const gfx::Insets& insets) const {
436 return orientation_ == kVertical ? insets.right() : insets.bottom();
437 }
438
439 int BoxLayout::MainAxisMarginBetweenViews(const ViewWrapper& leading,
440 const ViewWrapper& trailing) const {
441 if (!collapse_margins_spacing_ || !leading.view() || !trailing.view())
442 return between_child_spacing_;
443 return std::max(between_child_spacing_,
444 std::max(MainAxisTrailingInset(leading.margins()),
445 MainAxisLeadingInset(trailing.margins())));
446 }
447
448 gfx::Insets BoxLayout::MainAxisOuterMargin() const {
449 if (collapse_margins_spacing_) {
450 const ViewWrapper first(this, FirstVisibleView());
451 const ViewWrapper last(this, LastVisibleView());
452 return MaxAxisInsets(
453 orientation_ == kHorizontal ? HORIZONTAL_AXIS : VERTICAL_AXIS,
454 inside_border_insets_, first.margins(), inside_border_insets_,
455 last.margins());
456 }
457 return MaxAxisInsets(
458 orientation_ == kHorizontal ? HORIZONTAL_AXIS : VERTICAL_AXIS,
459 inside_border_insets_, gfx::Insets(), inside_border_insets_,
460 gfx::Insets());
461 }
462
463 gfx::Insets BoxLayout::CrossAxisMaxViewMargin() const {
464 int leading = 0;
465 int trailing = 0;
466 for (int i = 0; i < host_->child_count(); ++i) {
467 const ViewWrapper child(this, host_->child_at(i));
468 if (!child.visible())
469 continue;
470 leading = std::max(leading, CrossAxisLeadingInset(child.margins()));
471 trailing = std::max(trailing, CrossAxisTrailingInset(child.margins()));
472 }
473 if (orientation_ == Orientation::kVertical)
474 return gfx::Insets(0, leading, 0, trailing);
475 return gfx::Insets(leading, 0, trailing, 0);
476 }
477
478 void BoxLayout::AdjustMainAxisForMargin(gfx::Rect* rect) const {
479 rect->Inset(MainAxisOuterMargin());
480 }
481
482 void BoxLayout::AdjustCrossAxisForInsets(gfx::Rect* rect) const {
483 rect->Inset(orientation_ == Orientation::kVertical
484 ? gfx::Insets(0, inside_border_insets_.left(), 0,
485 inside_border_insets_.right())
486 : gfx::Insets(inside_border_insets_.top(), 0,
487 inside_border_insets_.bottom(), 0));
488 }
489
490 int BoxLayout::CrossAxisSizeForView(const ViewWrapper& view) const {
256 // TODO(bruthig): For horizontal case use the available width and not the 491 // TODO(bruthig): For horizontal case use the available width and not the
257 // preferred width. See https://crbug.com/682266. 492 // preferred width. See https://crbug.com/682266.
258 return orientation_ == kVertical 493 return orientation_ == kVertical
259 ? view->GetPreferredSize().width() 494 ? view.GetPreferredSize().width()
260 : view->GetHeightForWidth(view->GetPreferredSize().width()); 495 : view.GetHeightForWidth(view.GetPreferredSize().width());
496 }
497
498 int BoxLayout::CrossAxisMarginSizeForView(const ViewWrapper& view) const {
499 return collapse_margins_spacing_
500 ? 0
501 : (orientation_ == kVertical ? view.margins().width()
502 : view.margins().height());
503 }
504
505 int BoxLayout::CrossAxisLeadingMarginForView(const ViewWrapper& view) const {
506 return collapse_margins_spacing_ ? 0 : CrossAxisLeadingInset(view.margins());
507 }
508
509 void BoxLayout::InsetCrossAxis(gfx::Rect* rect,
510 int leading,
511 int trailing) const {
512 if (orientation_ == kVertical)
513 rect->Inset(leading, 0, trailing, 0);
514 else
515 rect->Inset(0, leading, 0, trailing);
261 } 516 }
262 517
263 gfx::Size BoxLayout::GetPreferredSizeForChildWidth(const View* host, 518 gfx::Size BoxLayout::GetPreferredSizeForChildWidth(const View* host,
264 int child_area_width) const { 519 int child_area_width) const {
520 DCHECK_EQ(host, host_);
265 gfx::Rect child_area_bounds; 521 gfx::Rect child_area_bounds;
266 522
267 if (orientation_ == kHorizontal) { 523 if (orientation_ == kHorizontal) {
268 // Horizontal layouts ignore |child_area_width|, meaning they mimic the 524 // Horizontal layouts ignore |child_area_width|, meaning they mimic the
269 // default behavior of GridLayout::GetPreferredHeightForWidth(). 525 // default behavior of GridLayout::GetPreferredHeightForWidth().
270 // TODO(estade|bruthig): Fix this See // https://crbug.com/682266. 526 // TODO(estade|bruthig): Fix this See // https://crbug.com/682266.
271 int position = 0; 527 int position = 0;
272 for (int i = 0; i < host->child_count(); ++i) { 528 gfx::Insets max_margins = CrossAxisMaxViewMargin();
273 const View* child = host->child_at(i); 529 for (int i = 0; i < host_->child_count(); ++i) {
274 if (!child->visible()) 530 const ViewWrapper child(this, host_->child_at(i));
531 if (!child.visible())
275 continue; 532 continue;
276 533
277 gfx::Size size(child->GetPreferredSize()); 534 gfx::Size size(child.view()->GetPreferredSize());
278 if (size.IsEmpty()) 535 if (size.IsEmpty())
279 continue; 536 continue;
280 537
281 gfx::Rect child_bounds(position, 0, size.width(), size.height()); 538 gfx::Rect child_bounds = gfx::Rect(
539 position, 0,
540 size.width() +
541 (!collapse_margins_spacing_ ? child.margins().width() : 0),
542 size.height());
543 gfx::Insets child_margins;
544 if (collapse_margins_spacing_)
545 child_margins =
546 MaxAxisInsets(VERTICAL_AXIS, child.margins(), inside_border_insets_,
547 child.margins(), inside_border_insets_);
548 else
549 child_margins = child.margins();
550
551 if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_START) {
552 child_bounds.Inset(0, -CrossAxisLeadingInset(max_margins), 0,
553 -child_margins.bottom());
554 child_bounds.set_origin(gfx::Point(position, 0));
555 } else if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_END) {
556 child_bounds.Inset(0, -child_margins.top(), 0,
557 -CrossAxisTrailingInset(max_margins));
558 child_bounds.set_origin(gfx::Point(position, 0));
559 } else {
560 child_bounds.set_origin(
561 gfx::Point(position, -(child_bounds.height() / 2)));
562 child_bounds.Inset(0, -child_margins.top(), 0, -child_margins.bottom());
563 }
564
282 child_area_bounds.Union(child_bounds); 565 child_area_bounds.Union(child_bounds);
283 position += size.width() + between_child_spacing_; 566 position += child_bounds.width() +
567 MainAxisMarginBetweenViews(
568 child, ViewWrapper(this, NextVisibleView(i)));
284 } 569 }
285 child_area_bounds.set_height( 570 child_area_bounds.set_height(
286 std::max(child_area_bounds.height(), minimum_cross_axis_size_)); 571 std::max(child_area_bounds.height(), minimum_cross_axis_size_));
287 } else { 572 } else {
288 int height = 0; 573 int height = 0;
289 for (int i = 0; i < host->child_count(); ++i) { 574 for (int i = 0; i < host_->child_count(); ++i) {
290 const View* child = host->child_at(i); 575 const ViewWrapper child(this, host_->child_at(i));
291 if (!child->visible()) 576 if (!child.visible())
292 continue; 577 continue;
293 578
579 const ViewWrapper next(this, NextVisibleView(i));
294 // Use the child area width for getting the height if the child is 580 // Use the child area width for getting the height if the child is
295 // supposed to stretch. Use its preferred size otherwise. 581 // supposed to stretch. Use its preferred size otherwise.
296 int extra_height = MainAxisSizeForView(child, child_area_width); 582 int extra_height = MainAxisSizeForView(child, child_area_width);
297 // Only add |between_child_spacing_| if this is not the only child. 583 // Only add |between_child_spacing_| if this is not the only child.
298 if (height != 0 && extra_height > 0) 584 if (next.view() && extra_height > 0)
299 height += between_child_spacing_; 585 height += MainAxisMarginBetweenViews(child, next);
300 height += extra_height; 586 height += extra_height;
301 } 587 }
302 588
303 child_area_bounds.set_width(child_area_width); 589 child_area_bounds.set_width(child_area_width);
304 child_area_bounds.set_height(height); 590 child_area_bounds.set_height(height);
305 } 591 }
306 592
307 gfx::Size non_child_size = NonChildSize(host); 593 gfx::Size non_child_size = NonChildSize(host_);
308 return gfx::Size(child_area_bounds.width() + non_child_size.width(), 594 return gfx::Size(child_area_bounds.width() + non_child_size.width(),
309 child_area_bounds.height() + non_child_size.height()); 595 child_area_bounds.height() + non_child_size.height());
310 } 596 }
311 597
312 gfx::Size BoxLayout::NonChildSize(const View* host) const { 598 gfx::Size BoxLayout::NonChildSize(const View* host) const {
313 gfx::Insets insets(host->GetInsets()); 599 gfx::Insets insets(host->GetInsets());
314 return gfx::Size(insets.width() + inside_border_insets_.width(), 600 if (!collapse_margins_spacing_)
315 insets.height() + inside_border_insets_.height()); 601 return gfx::Size(insets.width() + inside_border_insets_.width(),
602 insets.height() + inside_border_insets_.height());
603 gfx::Insets main_axis = MainAxisOuterMargin();
604 gfx::Insets cross_axis = inside_border_insets_;
605 return gfx::Size(insets.width() + main_axis.width() + cross_axis.width(),
606 insets.height() + main_axis.height() + cross_axis.height());
607 }
608
609 View* BoxLayout::NextVisibleView(int index) const {
610 for (int i = index + 1; i < host_->child_count(); ++i) {
611 View* result = host_->child_at(i);
612 if (result->visible())
613 return result;
614 }
615 return nullptr;
616 }
617
618 View* BoxLayout::FirstVisibleView() const {
619 return NextVisibleView(-1);
620 }
621
622 View* BoxLayout::LastVisibleView() const {
623 for (int i = host_->child_count() - 1; i >= 0; --i) {
624 View* result = host_->child_at(i);
625 if (result->visible())
626 return result;
627 }
628 return nullptr;
316 } 629 }
317 630
318 } // namespace views 631 } // namespace views
OLDNEW
« no previous file with comments | « ui/views/layout/box_layout.h ('k') | ui/views/layout/box_layout_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698