OLD | NEW |
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 return gfx::Insets(std::max(leading1.top(), leading2.top()), 0, |
| 31 std::max(trailing1.bottom(), trailing2.bottom()), 0); |
| 32 } |
| 33 } |
| 34 |
| 35 BoxLayout::ViewWrapper::ViewWrapper() |
| 36 : view_(nullptr), layout_(nullptr), orientation_(kHorizontal) {} |
| 37 |
| 38 BoxLayout::ViewWrapper::ViewWrapper(const BoxLayout* layout, View* view) |
| 39 : view_(view), layout_(layout), orientation_(layout_->orientation_) { |
| 40 gfx::Insets* margins = view_ ? view_->GetProperty(kMarginsKey) : nullptr; |
| 41 if (margins) |
| 42 margins_ = *margins; |
| 43 } |
| 44 |
| 45 BoxLayout::ViewWrapper::~ViewWrapper() {} |
| 46 |
| 47 int BoxLayout::ViewWrapper::GetHeightForWidth(int width) const { |
| 48 if (layout_->collapse_margins_spacing_) |
| 49 return view_->GetHeightForWidth(width); |
| 50 if (orientation_ == Orientation::kHorizontal) { |
| 51 return view_->GetHeightForWidth(std::max(0, width - margins_.width())) + |
| 52 margins_.height(); |
| 53 } else { |
| 54 return view_->GetHeightForWidth(width) + margins_.height(); |
| 55 } |
| 56 } |
| 57 |
| 58 gfx::Size BoxLayout::ViewWrapper::GetPreferredSize() const { |
| 59 gfx::Size preferred_size = view_->GetPreferredSize(); |
| 60 if (!layout_->collapse_margins_spacing_) |
| 61 preferred_size.Enlarge(margins_.width(), margins_.height()); |
| 62 return preferred_size; |
| 63 } |
| 64 |
| 65 void BoxLayout::ViewWrapper::SetBoundsRect(const gfx::Rect& bounds) { |
| 66 gfx::Rect new_bounds = bounds; |
| 67 if (!layout_->collapse_margins_spacing_) { |
| 68 if (orientation_ == Orientation::kHorizontal) { |
| 69 new_bounds.set_x(bounds.x() + margins_.left()); |
| 70 new_bounds.set_width(std::max(0, bounds.width() - margins_.width())); |
| 71 } |
| 72 if (orientation_ == Orientation::kVertical) { |
| 73 new_bounds.set_y(bounds.y() + margins_.top()); |
| 74 new_bounds.set_height(std::max(0, bounds.height() - margins_.height())); |
| 75 } |
| 76 } |
| 77 view_->SetBoundsRect(new_bounds); |
| 78 } |
| 79 |
| 80 bool BoxLayout::ViewWrapper::visible() const { |
| 81 return view_->visible(); |
| 82 } |
| 83 |
14 BoxLayout::BoxLayout(BoxLayout::Orientation orientation, | 84 BoxLayout::BoxLayout(BoxLayout::Orientation orientation, |
15 int inside_border_horizontal_spacing, | 85 int inside_border_horizontal_spacing, |
16 int inside_border_vertical_spacing, | 86 int inside_border_vertical_spacing, |
17 int between_child_spacing) | 87 int between_child_spacing, |
| 88 bool collapse_margins_spacing) |
18 : orientation_(orientation), | 89 : orientation_(orientation), |
19 inside_border_insets_(inside_border_vertical_spacing, | 90 inside_border_insets_(inside_border_vertical_spacing, |
20 inside_border_horizontal_spacing), | 91 inside_border_horizontal_spacing), |
21 between_child_spacing_(between_child_spacing), | 92 between_child_spacing_(between_child_spacing), |
22 main_axis_alignment_(MAIN_AXIS_ALIGNMENT_START), | 93 main_axis_alignment_(MAIN_AXIS_ALIGNMENT_START), |
23 cross_axis_alignment_(CROSS_AXIS_ALIGNMENT_STRETCH), | 94 cross_axis_alignment_(CROSS_AXIS_ALIGNMENT_STRETCH), |
24 default_flex_(0), | 95 default_flex_(0), |
25 minimum_cross_axis_size_(0), | 96 minimum_cross_axis_size_(0), |
26 host_(NULL) { | 97 collapse_margins_spacing_(collapse_margins_spacing), |
27 } | 98 host_(NULL) {} |
28 | 99 |
29 BoxLayout::~BoxLayout() { | 100 BoxLayout::~BoxLayout() { |
30 } | 101 } |
31 | 102 |
32 void BoxLayout::SetFlexForView(const View* view, int flex_weight) { | 103 void BoxLayout::SetFlexForView(const View* view, int flex_weight) { |
33 DCHECK(host_); | 104 DCHECK(host_); |
34 DCHECK(view); | 105 DCHECK(view); |
35 DCHECK_EQ(host_, view->parent()); | 106 DCHECK_EQ(host_, view->parent()); |
36 DCHECK_GE(flex_weight, 0); | 107 DCHECK_GE(flex_weight, 0); |
37 flex_map_[view] = flex_weight; | 108 flex_map_[view] = flex_weight; |
38 } | 109 } |
39 | 110 |
40 void BoxLayout::ClearFlexForView(const View* view) { | 111 void BoxLayout::ClearFlexForView(const View* view) { |
41 DCHECK(view); | 112 DCHECK(view); |
42 flex_map_.erase(view); | 113 flex_map_.erase(view); |
43 } | 114 } |
44 | 115 |
45 void BoxLayout::SetDefaultFlex(int default_flex) { | 116 void BoxLayout::SetDefaultFlex(int default_flex) { |
46 DCHECK_GE(default_flex, 0); | 117 DCHECK_GE(default_flex, 0); |
47 default_flex_ = default_flex; | 118 default_flex_ = default_flex; |
48 } | 119 } |
49 | 120 |
50 void BoxLayout::Layout(View* host) { | 121 void BoxLayout::Layout(View* host) { |
51 DCHECK_EQ(host_, host); | 122 DCHECK_EQ(host_, host); |
52 gfx::Rect child_area(host->GetLocalBounds()); | 123 gfx::Rect child_area(host->GetContentsBounds()); |
53 child_area.Inset(host->GetInsets()); | 124 |
54 child_area.Inset(inside_border_insets_); | 125 AdjustMainAxisForMargin(&child_area); |
| 126 gfx::Insets max_cross_axis_margin; |
| 127 if (!collapse_margins_spacing_) { |
| 128 AdjustCrossAxisForInsets(&child_area); |
| 129 max_cross_axis_margin = CrossAxisMaxViewMargin(); |
| 130 } |
55 | 131 |
56 int total_main_axis_size = 0; | 132 int total_main_axis_size = 0; |
57 int num_visible = 0; | 133 int num_visible = 0; |
58 int flex_sum = 0; | 134 int flex_sum = 0; |
59 // Calculate the total size of children in the main axis. | 135 // Calculate the total size of children in the main axis. |
60 for (int i = 0; i < host->child_count(); ++i) { | 136 for (int i = 0; i < host->child_count(); ++i) { |
61 View* child = host->child_at(i); | 137 const ViewWrapper child(this, host->child_at(i)); |
62 if (!child->visible()) | 138 if (!child.visible()) |
63 continue; | 139 continue; |
64 int flex = GetFlexForView(child); | 140 int flex = GetFlexForView(child.view()); |
65 int child_main_axis_size = MainAxisSizeForView(child, child_area.width()); | 141 int child_main_axis_size = MainAxisSizeForView(child, child_area.width()); |
66 if (child_main_axis_size == 0 && flex == 0) | 142 if (child_main_axis_size == 0 && flex == 0) |
67 continue; | 143 continue; |
68 total_main_axis_size += child_main_axis_size + between_child_spacing_; | 144 total_main_axis_size += child_main_axis_size + |
| 145 MainAxisMarginBetweenViews( |
| 146 child, ViewWrapper(this, NextVisibleView(i))); |
69 ++num_visible; | 147 ++num_visible; |
70 flex_sum += flex; | 148 flex_sum += flex; |
71 } | 149 } |
72 | 150 |
73 if (!num_visible) | 151 if (!num_visible) |
74 return; | 152 return; |
75 | 153 |
76 total_main_axis_size -= between_child_spacing_; | 154 total_main_axis_size -= between_child_spacing_; |
77 // Free space can be negative indicating that the views want to overflow. | 155 // Free space can be negative indicating that the views want to overflow. |
78 int main_free_space = MainAxisSize(child_area) - total_main_axis_size; | 156 int main_free_space = MainAxisSize(child_area) - total_main_axis_size; |
(...skipping 20 matching lines...) Expand all Loading... |
99 gfx::Rect new_child_area(child_area); | 177 gfx::Rect new_child_area(child_area); |
100 SetMainAxisPosition(position, &new_child_area); | 178 SetMainAxisPosition(position, &new_child_area); |
101 SetMainAxisSize(size, &new_child_area); | 179 SetMainAxisSize(size, &new_child_area); |
102 child_area.Intersect(new_child_area); | 180 child_area.Intersect(new_child_area); |
103 } | 181 } |
104 | 182 |
105 int main_position = MainAxisPosition(child_area); | 183 int main_position = MainAxisPosition(child_area); |
106 int total_padding = 0; | 184 int total_padding = 0; |
107 int current_flex = 0; | 185 int current_flex = 0; |
108 for (int i = 0; i < host->child_count(); ++i) { | 186 for (int i = 0; i < host->child_count(); ++i) { |
109 View* child = host->child_at(i); | 187 ViewWrapper child(this, host->child_at(i)); |
110 if (!child->visible()) | 188 if (!child.visible()) |
111 continue; | 189 continue; |
112 | 190 |
113 // TODO(bruthig): Fix this. The main axis should be calculated before | 191 // TODO(bruthig): Fix this. The main axis should be calculated before |
114 // the cross axis size because child Views may calculate their cross axis | 192 // the cross axis size because child Views may calculate their cross axis |
115 // size based on their main axis size. See https://crbug.com/682266. | 193 // size based on their main axis size. See https://crbug.com/682266. |
116 | 194 |
117 // Calculate cross axis size. | 195 // Calculate cross axis size. |
118 gfx::Rect bounds(child_area); | 196 gfx::Rect bounds(child_area); |
| 197 gfx::Rect min_child_area(child_area); |
| 198 gfx::Insets child_margins; |
| 199 if (collapse_margins_spacing_) |
| 200 child_margins = MaxAxisInsets( |
| 201 orientation_ == kVertical ? HORIZONTAL_AXIS : VERTICAL_AXIS, |
| 202 child.margins(), inside_border_insets_, child.margins(), |
| 203 inside_border_insets_); |
| 204 else |
| 205 child_margins = child.margins(); |
| 206 if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_STRETCH || |
| 207 cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_CENTER) { |
| 208 InsetCrossAxis(&min_child_area, CrossAxisLeadingInset(child_margins), |
| 209 CrossAxisTrailingInset(child_margins)); |
| 210 } |
| 211 |
119 SetMainAxisPosition(main_position, &bounds); | 212 SetMainAxisPosition(main_position, &bounds); |
120 if (cross_axis_alignment_ != CROSS_AXIS_ALIGNMENT_STRETCH) { | 213 if (cross_axis_alignment_ != CROSS_AXIS_ALIGNMENT_STRETCH) { |
121 int free_space = CrossAxisSize(bounds) - CrossAxisSizeForView(child); | 214 int cross_axis_margin_size = CrossAxisMarginSizeForView(child); |
| 215 int view_cross_axis_size = |
| 216 CrossAxisSizeForView(child) - cross_axis_margin_size; |
| 217 int free_space = CrossAxisSize(bounds) - view_cross_axis_size; |
122 int position = CrossAxisPosition(bounds); | 218 int position = CrossAxisPosition(bounds); |
123 if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_CENTER) { | 219 if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_CENTER) { |
| 220 if (view_cross_axis_size > CrossAxisSize(min_child_area)) |
| 221 view_cross_axis_size = CrossAxisSize(min_child_area); |
124 position += free_space / 2; | 222 position += free_space / 2; |
| 223 position = std::max(position, CrossAxisLeadingEdge(min_child_area)); |
125 } else if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_END) { | 224 } else if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_END) { |
126 position += free_space; | 225 position += free_space - CrossAxisTrailingInset(max_cross_axis_margin); |
| 226 if (!collapse_margins_spacing_) |
| 227 InsetCrossAxis(&min_child_area, |
| 228 CrossAxisLeadingInset(child.margins()), |
| 229 CrossAxisTrailingInset(max_cross_axis_margin)); |
| 230 } else { |
| 231 position += CrossAxisLeadingInset(max_cross_axis_margin); |
| 232 if (!collapse_margins_spacing_) |
| 233 InsetCrossAxis(&min_child_area, |
| 234 CrossAxisLeadingInset(max_cross_axis_margin), |
| 235 CrossAxisTrailingInset(child.margins())); |
127 } | 236 } |
128 SetCrossAxisPosition(position, &bounds); | 237 SetCrossAxisPosition(position, &bounds); |
129 SetCrossAxisSize(CrossAxisSizeForView(child), &bounds); | 238 SetCrossAxisSize(view_cross_axis_size, &bounds); |
130 } | 239 } |
131 | 240 |
132 // Calculate flex padding. | 241 // Calculate flex padding. |
133 int current_padding = 0; | 242 int current_padding = 0; |
134 if (GetFlexForView(child) > 0) { | 243 if (GetFlexForView(child.view()) > 0) { |
135 current_flex += GetFlexForView(child); | 244 current_flex += GetFlexForView(child.view()); |
136 int quot = (main_free_space * current_flex) / flex_sum; | 245 int quot = (main_free_space * current_flex) / flex_sum; |
137 int rem = (main_free_space * current_flex) % flex_sum; | 246 int rem = (main_free_space * current_flex) % flex_sum; |
138 current_padding = quot - total_padding; | 247 current_padding = quot - total_padding; |
139 // Use the current remainder to round to the nearest pixel. | 248 // Use the current remainder to round to the nearest pixel. |
140 if (std::abs(rem) * 2 >= flex_sum) | 249 if (std::abs(rem) * 2 >= flex_sum) |
141 current_padding += main_free_space > 0 ? 1 : -1; | 250 current_padding += main_free_space > 0 ? 1 : -1; |
142 total_padding += current_padding; | 251 total_padding += current_padding; |
143 } | 252 } |
144 | 253 |
145 // Set main axis size. | 254 // Set main axis size. |
146 // TODO(bruthig): Use the allocated width to determine the cross axis size. | 255 // TODO(bruthig): Use the allocated width to determine the cross axis size. |
147 // See https://crbug.com/682266. | 256 // See https://crbug.com/682266. |
148 int child_main_axis_size = MainAxisSizeForView(child, child_area.width()); | 257 int child_main_axis_size = MainAxisSizeForView(child, child_area.width()); |
149 SetMainAxisSize(child_main_axis_size + current_padding, &bounds); | 258 SetMainAxisSize(child_main_axis_size + current_padding, &bounds); |
150 if (MainAxisSize(bounds) > 0 || GetFlexForView(child) > 0) | 259 if (MainAxisSize(bounds) > 0 || GetFlexForView(child.view()) > 0) |
151 main_position += MainAxisSize(bounds) + between_child_spacing_; | 260 main_position += MainAxisSize(bounds) + |
| 261 MainAxisMarginBetweenViews( |
| 262 child, ViewWrapper(this, NextVisibleView(i))); |
152 | 263 |
153 // Clamp child view bounds to |child_area|. | 264 // Clamp child view bounds to |child_area|. |
154 bounds.Intersect(child_area); | 265 bounds.Intersect(min_child_area); |
155 child->SetBoundsRect(bounds); | 266 child.SetBoundsRect(bounds); |
156 } | 267 } |
157 | 268 |
158 // Flex views should have grown/shrunk to consume all free space. | 269 // Flex views should have grown/shrunk to consume all free space. |
159 if (flex_sum) | 270 if (flex_sum) |
160 DCHECK_EQ(total_padding, main_free_space); | 271 DCHECK_EQ(total_padding, main_free_space); |
161 } | 272 } |
162 | 273 |
163 gfx::Size BoxLayout::GetPreferredSize(const View* host) const { | 274 gfx::Size BoxLayout::GetPreferredSize(const View* host) const { |
164 DCHECK_EQ(host_, host); | 275 DCHECK_EQ(host_, host); |
165 // Calculate the child views' preferred width. | 276 // Calculate the child views' preferred width. |
166 int width = 0; | 277 int width = 0; |
167 if (orientation_ == kVertical) { | 278 if (orientation_ == kVertical) { |
168 for (int i = 0; i < host->child_count(); ++i) { | 279 int leading = 0; |
169 const View* child = host->child_at(i); | 280 int trailing = 0; |
170 if (!child->visible()) | 281 gfx::Rect child_view_area; |
| 282 for (int i = 0; i < host_->child_count(); ++i) { |
| 283 const ViewWrapper child(this, host_->child_at(i)); |
| 284 if (!child.visible()) |
171 continue; | 285 continue; |
172 | 286 |
173 width = std::max(width, child->GetPreferredSize().width()); | 287 gfx::Size child_size = child.view()->GetPreferredSize(); |
| 288 gfx::Insets child_margins; |
| 289 if (collapse_margins_spacing_) |
| 290 child_margins = MaxAxisInsets(HORIZONTAL_AXIS, child.margins(), |
| 291 inside_border_insets_, child.margins(), |
| 292 inside_border_insets_); |
| 293 else |
| 294 child_margins = child.margins(); |
| 295 if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_START) { |
| 296 leading = std::max(leading, CrossAxisLeadingInset(child_margins)); |
| 297 width = std::max( |
| 298 width, child_size.width() + CrossAxisTrailingInset(child_margins)); |
| 299 } else if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_END) { |
| 300 trailing = std::max(trailing, CrossAxisTrailingInset(child_margins)); |
| 301 width = std::max( |
| 302 width, child_size.width() + CrossAxisLeadingInset(child_margins)); |
| 303 } else { |
| 304 gfx::Rect child_bounds = |
| 305 gfx::Rect(-(child_size.width() / 2), 0, child_size.width(), |
| 306 child_size.height()); |
| 307 child_bounds.Inset(-child.margins().left(), 0, -child.margins().right(), |
| 308 0); |
| 309 child_view_area.Union(child_bounds); |
| 310 width = std::max(width, child_view_area.width()); |
| 311 } |
174 } | 312 } |
175 width = std::max(width, minimum_cross_axis_size_); | 313 width = std::max(width + leading + trailing, minimum_cross_axis_size_); |
176 } | 314 } |
177 | 315 |
178 return GetPreferredSizeForChildWidth(host, width); | 316 return GetPreferredSizeForChildWidth(host, width); |
179 } | 317 } |
180 | 318 |
181 int BoxLayout::GetPreferredHeightForWidth(const View* host, int width) const { | 319 int BoxLayout::GetPreferredHeightForWidth(const View* host, int width) const { |
182 DCHECK_EQ(host_, host); | 320 DCHECK_EQ(host_, host); |
183 int child_width = width - NonChildSize(host).width(); | 321 int child_width = width - NonChildSize(host).width(); |
184 return GetPreferredSizeForChildWidth(host, child_width).height(); | 322 return GetPreferredSizeForChildWidth(host, child_width).height(); |
185 } | 323 } |
186 | 324 |
187 void BoxLayout::Installed(View* host) { | 325 void BoxLayout::Installed(View* host) { |
188 DCHECK(!host_); | 326 DCHECK(!host_); |
189 host_ = host; | 327 host_ = host; |
190 } | 328 } |
191 | 329 |
192 void BoxLayout::ViewRemoved(View* host, View* view) { | 330 void BoxLayout::ViewRemoved(View* host, View* view) { |
193 ClearFlexForView(view); | 331 ClearFlexForView(view); |
194 } | 332 } |
195 | 333 |
196 int BoxLayout::GetFlexForView(const View* view) const { | 334 int BoxLayout::GetFlexForView(const View* view) const { |
197 std::map<const View*, int>::const_iterator it = flex_map_.find(view); | 335 FlexMap::const_iterator it = flex_map_.find(view); |
198 if (it == flex_map_.end()) | 336 if (it == flex_map_.end()) |
199 return default_flex_; | 337 return default_flex_; |
200 | 338 |
201 return it->second; | 339 return it->second; |
202 } | 340 } |
203 | 341 |
204 int BoxLayout::MainAxisSize(const gfx::Rect& rect) const { | 342 int BoxLayout::MainAxisSize(const gfx::Rect& rect) const { |
205 return orientation_ == kHorizontal ? rect.width() : rect.height(); | 343 return orientation_ == kHorizontal ? rect.width() : rect.height(); |
206 } | 344 } |
207 | 345 |
(...skipping 30 matching lines...) Expand all Loading... |
238 rect->set_height(size); | 376 rect->set_height(size); |
239 } | 377 } |
240 | 378 |
241 void BoxLayout::SetCrossAxisPosition(int position, gfx::Rect* rect) const { | 379 void BoxLayout::SetCrossAxisPosition(int position, gfx::Rect* rect) const { |
242 if (orientation_ == kVertical) | 380 if (orientation_ == kVertical) |
243 rect->set_x(position); | 381 rect->set_x(position); |
244 else | 382 else |
245 rect->set_y(position); | 383 rect->set_y(position); |
246 } | 384 } |
247 | 385 |
248 int BoxLayout::MainAxisSizeForView(const View* view, | 386 int BoxLayout::MainAxisSizeForView(const ViewWrapper& view, |
249 int child_area_width) const { | 387 int child_area_width) const { |
250 return orientation_ == kHorizontal | 388 return orientation_ == kHorizontal |
251 ? view->GetPreferredSize().width() | 389 ? view.GetPreferredSize().width() |
252 : view->GetHeightForWidth(cross_axis_alignment_ == | 390 : view.GetHeightForWidth(cross_axis_alignment_ == |
253 CROSS_AXIS_ALIGNMENT_STRETCH | 391 CROSS_AXIS_ALIGNMENT_STRETCH |
254 ? child_area_width | 392 ? child_area_width |
255 : view->GetPreferredSize().width()); | 393 : view.GetPreferredSize().width()); |
256 } | 394 } |
257 | 395 |
258 int BoxLayout::CrossAxisSizeForView(const View* view) const { | 396 int BoxLayout::MainAxisLeadingInset(const gfx::Insets& insets) const { |
| 397 return orientation_ == kHorizontal ? insets.left() : insets.top(); |
| 398 } |
| 399 |
| 400 int BoxLayout::MainAxisTrailingInset(const gfx::Insets& insets) const { |
| 401 return orientation_ == kHorizontal ? insets.right() : insets.bottom(); |
| 402 } |
| 403 |
| 404 int BoxLayout::CrossAxisLeadingEdge(const gfx::Rect& rect) const { |
| 405 return orientation_ == kVertical ? rect.x() : rect.y(); |
| 406 } |
| 407 |
| 408 int BoxLayout::CrossAxisLeadingInset(const gfx::Insets& insets) const { |
| 409 return orientation_ == kVertical ? insets.left() : insets.top(); |
| 410 } |
| 411 |
| 412 int BoxLayout::CrossAxisTrailingInset(const gfx::Insets& insets) const { |
| 413 return orientation_ == kVertical ? insets.right() : insets.bottom(); |
| 414 } |
| 415 |
| 416 int BoxLayout::MainAxisMarginBetweenViews(const ViewWrapper& leading, |
| 417 const ViewWrapper& trailing) const { |
| 418 if (!collapse_margins_spacing_ || !leading.view() || !trailing.view()) |
| 419 return between_child_spacing_; |
| 420 return std::max(between_child_spacing_, |
| 421 std::max(MainAxisTrailingInset(leading.margins()), |
| 422 MainAxisLeadingInset(trailing.margins()))); |
| 423 } |
| 424 |
| 425 gfx::Insets BoxLayout::MainAxisOuterMargin() const { |
| 426 if (collapse_margins_spacing_) { |
| 427 const ViewWrapper first(this, FirstVisibleView()); |
| 428 const ViewWrapper last(this, LastVisibleView()); |
| 429 return MaxAxisInsets( |
| 430 orientation_ == kHorizontal ? HORIZONTAL_AXIS : VERTICAL_AXIS, |
| 431 inside_border_insets_, first.margins(), inside_border_insets_, |
| 432 last.margins()); |
| 433 } |
| 434 return MaxAxisInsets( |
| 435 orientation_ == kHorizontal ? HORIZONTAL_AXIS : VERTICAL_AXIS, |
| 436 inside_border_insets_, gfx::Insets(), inside_border_insets_, |
| 437 gfx::Insets()); |
| 438 } |
| 439 |
| 440 gfx::Insets BoxLayout::CrossAxisMaxViewMargin() const { |
| 441 int leading = 0; |
| 442 int trailing = 0; |
| 443 for (int i = 0; i < host_->child_count(); ++i) { |
| 444 const ViewWrapper child(this, host_->child_at(i)); |
| 445 if (!child.visible()) |
| 446 continue; |
| 447 leading = std::max(leading, CrossAxisLeadingInset(child.margins())); |
| 448 trailing = std::max(trailing, CrossAxisTrailingInset(child.margins())); |
| 449 } |
| 450 if (orientation_ == Orientation::kVertical) |
| 451 return gfx::Insets(0, leading, 0, trailing); |
| 452 return gfx::Insets(leading, 0, trailing, 0); |
| 453 } |
| 454 |
| 455 void BoxLayout::AdjustMainAxisForMargin(gfx::Rect* rect) const { |
| 456 rect->Inset(MainAxisOuterMargin()); |
| 457 } |
| 458 |
| 459 void BoxLayout::AdjustCrossAxisForInsets(gfx::Rect* rect) const { |
| 460 rect->Inset(orientation_ == Orientation::kVertical |
| 461 ? gfx::Insets(0, inside_border_insets_.left(), 0, |
| 462 inside_border_insets_.right()) |
| 463 : gfx::Insets(inside_border_insets_.top(), 0, |
| 464 inside_border_insets_.bottom(), 0)); |
| 465 } |
| 466 |
| 467 int BoxLayout::CrossAxisSizeForView(const ViewWrapper& view) const { |
259 // TODO(bruthig): For horizontal case use the available width and not the | 468 // TODO(bruthig): For horizontal case use the available width and not the |
260 // preferred width. See https://crbug.com/682266. | 469 // preferred width. See https://crbug.com/682266. |
261 return orientation_ == kVertical | 470 return orientation_ == kVertical |
262 ? view->GetPreferredSize().width() | 471 ? view.GetPreferredSize().width() |
263 : view->GetHeightForWidth(view->GetPreferredSize().width()); | 472 : view.GetHeightForWidth(view.GetPreferredSize().width()); |
| 473 } |
| 474 |
| 475 int BoxLayout::CrossAxisMarginSizeForView(const ViewWrapper& view) const { |
| 476 return collapse_margins_spacing_ |
| 477 ? 0 |
| 478 : (orientation_ == kVertical ? view.margins().width() |
| 479 : view.margins().height()); |
| 480 } |
| 481 |
| 482 int BoxLayout::CrossAxisLeadingMarginForView(const ViewWrapper& view) const { |
| 483 return collapse_margins_spacing_ ? 0 : CrossAxisLeadingInset(view.margins()); |
| 484 } |
| 485 |
| 486 void BoxLayout::InsetCrossAxis(gfx::Rect* rect, |
| 487 int leading, |
| 488 int trailing) const { |
| 489 if (orientation_ == kVertical) |
| 490 rect->Inset(leading, 0, trailing, 0); |
| 491 else |
| 492 rect->Inset(0, leading, 0, trailing); |
264 } | 493 } |
265 | 494 |
266 gfx::Size BoxLayout::GetPreferredSizeForChildWidth(const View* host, | 495 gfx::Size BoxLayout::GetPreferredSizeForChildWidth(const View* host, |
267 int child_area_width) const { | 496 int child_area_width) const { |
| 497 DCHECK_EQ(host, host_); |
268 gfx::Rect child_area_bounds; | 498 gfx::Rect child_area_bounds; |
269 | 499 |
270 if (orientation_ == kHorizontal) { | 500 if (orientation_ == kHorizontal) { |
271 // Horizontal layouts ignore |child_area_width|, meaning they mimic the | 501 // Horizontal layouts ignore |child_area_width|, meaning they mimic the |
272 // default behavior of GridLayout::GetPreferredHeightForWidth(). | 502 // default behavior of GridLayout::GetPreferredHeightForWidth(). |
273 // TODO(estade|bruthig): Fix this See // https://crbug.com/682266. | 503 // TODO(estade|bruthig): Fix this See // https://crbug.com/682266. |
274 int position = 0; | 504 int position = 0; |
275 for (int i = 0; i < host->child_count(); ++i) { | 505 gfx::Insets max_margins = CrossAxisMaxViewMargin(); |
276 const View* child = host->child_at(i); | 506 for (int i = 0; i < host_->child_count(); ++i) { |
277 if (!child->visible()) | 507 const ViewWrapper child(this, host_->child_at(i)); |
| 508 if (!child.visible()) |
278 continue; | 509 continue; |
279 | 510 |
280 gfx::Size size(child->GetPreferredSize()); | 511 gfx::Size size(child.view()->GetPreferredSize()); |
281 if (size.IsEmpty()) | 512 if (size.IsEmpty()) |
282 continue; | 513 continue; |
283 | 514 |
284 gfx::Rect child_bounds(position, 0, size.width(), size.height()); | 515 gfx::Rect child_bounds = gfx::Rect( |
| 516 position, 0, |
| 517 size.width() + |
| 518 (!collapse_margins_spacing_ ? child.margins().width() : 0), |
| 519 size.height()); |
| 520 gfx::Insets child_margins; |
| 521 if (collapse_margins_spacing_) |
| 522 child_margins = |
| 523 MaxAxisInsets(VERTICAL_AXIS, child.margins(), inside_border_insets_, |
| 524 child.margins(), inside_border_insets_); |
| 525 else |
| 526 child_margins = child.margins(); |
| 527 |
| 528 if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_START) { |
| 529 child_bounds.Inset(0, -CrossAxisLeadingInset(max_margins), 0, |
| 530 -child_margins.bottom()); |
| 531 child_bounds.set_origin(gfx::Point(position, 0)); |
| 532 } else if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_END) { |
| 533 child_bounds.Inset(0, -child_margins.top(), 0, |
| 534 -CrossAxisTrailingInset(max_margins)); |
| 535 child_bounds.set_origin(gfx::Point(position, 0)); |
| 536 } else { |
| 537 child_bounds.set_origin( |
| 538 gfx::Point(position, -(child_bounds.height() / 2))); |
| 539 child_bounds.Inset(0, -child_margins.top(), 0, -child_margins.bottom()); |
| 540 } |
| 541 |
285 child_area_bounds.Union(child_bounds); | 542 child_area_bounds.Union(child_bounds); |
286 position += size.width() + between_child_spacing_; | 543 position += child_bounds.width() + |
| 544 MainAxisMarginBetweenViews( |
| 545 child, ViewWrapper(this, NextVisibleView(i))); |
287 } | 546 } |
288 child_area_bounds.set_height( | 547 child_area_bounds.set_height( |
289 std::max(child_area_bounds.height(), minimum_cross_axis_size_)); | 548 std::max(child_area_bounds.height(), minimum_cross_axis_size_)); |
290 } else { | 549 } else { |
291 int height = 0; | 550 int height = 0; |
292 for (int i = 0; i < host->child_count(); ++i) { | 551 for (int i = 0; i < host_->child_count(); ++i) { |
293 const View* child = host->child_at(i); | 552 const ViewWrapper child(this, host_->child_at(i)); |
294 if (!child->visible()) | 553 if (!child.visible()) |
295 continue; | 554 continue; |
296 | 555 |
| 556 const ViewWrapper next(this, NextVisibleView(i)); |
297 // Use the child area width for getting the height if the child is | 557 // Use the child area width for getting the height if the child is |
298 // supposed to stretch. Use its preferred size otherwise. | 558 // supposed to stretch. Use its preferred size otherwise. |
299 int extra_height = MainAxisSizeForView(child, child_area_width); | 559 int extra_height = MainAxisSizeForView(child, child_area_width); |
300 // Only add |between_child_spacing_| if this is not the only child. | 560 // Only add |between_child_spacing_| if this is not the only child. |
301 if (height != 0 && extra_height > 0) | 561 if (next.view() && extra_height > 0) |
302 height += between_child_spacing_; | 562 height += MainAxisMarginBetweenViews(child, next); |
303 height += extra_height; | 563 height += extra_height; |
304 } | 564 } |
305 | 565 |
306 child_area_bounds.set_width(child_area_width); | 566 child_area_bounds.set_width(child_area_width); |
307 child_area_bounds.set_height(height); | 567 child_area_bounds.set_height(height); |
308 } | 568 } |
309 | 569 |
310 gfx::Size non_child_size = NonChildSize(host); | 570 gfx::Size non_child_size = NonChildSize(host_); |
311 return gfx::Size(child_area_bounds.width() + non_child_size.width(), | 571 return gfx::Size(child_area_bounds.width() + non_child_size.width(), |
312 child_area_bounds.height() + non_child_size.height()); | 572 child_area_bounds.height() + non_child_size.height()); |
313 } | 573 } |
314 | 574 |
315 gfx::Size BoxLayout::NonChildSize(const View* host) const { | 575 gfx::Size BoxLayout::NonChildSize(const View* host) const { |
316 gfx::Insets insets(host->GetInsets()); | 576 gfx::Insets insets(host->GetInsets()); |
317 return gfx::Size(insets.width() + inside_border_insets_.width(), | 577 if (!collapse_margins_spacing_) |
318 insets.height() + inside_border_insets_.height()); | 578 return gfx::Size(insets.width() + inside_border_insets_.width(), |
| 579 insets.height() + inside_border_insets_.height()); |
| 580 gfx::Insets main_axis = MainAxisOuterMargin(); |
| 581 gfx::Insets cross_axis = inside_border_insets_; |
| 582 return gfx::Size(insets.width() + main_axis.width() + cross_axis.width(), |
| 583 insets.height() + main_axis.height() + cross_axis.height()); |
| 584 } |
| 585 |
| 586 View* BoxLayout::NextVisibleView(int index) const { |
| 587 for (int i = index + 1; i < host_->child_count(); ++i) { |
| 588 View* result = host_->child_at(i); |
| 589 if (result->visible()) |
| 590 return result; |
| 591 } |
| 592 return nullptr; |
| 593 } |
| 594 |
| 595 View* BoxLayout::FirstVisibleView() const { |
| 596 return NextVisibleView(-1); |
| 597 } |
| 598 |
| 599 View* BoxLayout::LastVisibleView() const { |
| 600 for (int i = host_->child_count() - 1; i >= 0; --i) { |
| 601 View* result = host_->child_at(i); |
| 602 if (result->visible()) |
| 603 return result; |
| 604 } |
| 605 return nullptr; |
319 } | 606 } |
320 | 607 |
321 } // namespace views | 608 } // namespace views |
OLD | NEW |