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 } |
| 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 Loading... |
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 Loading... |
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 |
OLD | NEW |