Chromium Code Reviews| 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 | 11 |
| 12 namespace views { | 12 namespace views { |
| 13 | 13 |
| 14 BoxLayout::ViewWrapper::ViewWrapper() | |
| 15 : view_(nullptr), layout_(nullptr), margins_(0, 0, 0, 0){}; | |
|
sky
2017/04/25 17:28:58
(0,0,0,0) is the default, so need to specify margi
kylix_rd
2017/04/26 17:41:33
Done.
| |
| 16 | |
| 17 BoxLayout::ViewWrapper::ViewWrapper(const BoxLayout* layout, const View* view) | |
| 18 : view_(view), layout_(layout), margins_(layout_->GetViewMargins(view)) {} | |
| 19 | |
| 20 BoxLayout::ViewWrapper::ViewWrapper(const ViewWrapper& wrapper) | |
|
sky
2017/04/25 17:28:58
= default?
kylix_rd
2017/04/26 17:41:33
Done.
| |
| 21 : view_(wrapper.view_), | |
| 22 layout_(wrapper.layout_), | |
| 23 margins_(wrapper.margins_) {} | |
| 24 | |
| 25 BoxLayout::ViewWrapper::~ViewWrapper() {} | |
| 26 | |
| 27 gfx::Size BoxLayout::ViewWrapper::GetPreferredSize() const { | |
| 28 gfx::Size preferred_size = view_->GetPreferredSize(); | |
| 29 preferred_size.Enlarge(margins_.width(), margins_.height()); | |
| 30 return preferred_size; | |
| 31 } | |
| 32 | |
| 33 void BoxLayout::ViewWrapper::SetBoundsRect(const gfx::Rect& bounds) { | |
| 34 const_cast<View*>(view_)->SetBounds( | |
|
sky
2017/04/25 17:28:58
Why the const_cast here?
kylix_rd
2017/04/26 17:41:33
The view_ field is declared as "const View* view_;
sky
2017/04/26 19:57:07
If you're going to const_cast, why make the member
| |
| 35 bounds.x() + margins_.left(), bounds.y() + margins_.top(), | |
| 36 bounds.width() - margins_.width(), bounds.height() - margins_.height()); | |
| 37 } | |
| 38 | |
| 39 bool BoxLayout::ViewWrapper::visible() const { | |
| 40 return view_->visible(); | |
| 41 } | |
| 42 | |
| 14 BoxLayout::BoxLayout(BoxLayout::Orientation orientation, | 43 BoxLayout::BoxLayout(BoxLayout::Orientation orientation, |
| 15 int inside_border_horizontal_spacing, | 44 int inside_border_horizontal_spacing, |
| 16 int inside_border_vertical_spacing, | 45 int inside_border_vertical_spacing, |
| 17 int between_child_spacing) | 46 int between_child_spacing) |
| 18 : orientation_(orientation), | 47 : orientation_(orientation), |
| 19 inside_border_insets_(inside_border_vertical_spacing, | 48 inside_border_insets_(inside_border_vertical_spacing, |
| 20 inside_border_horizontal_spacing), | 49 inside_border_horizontal_spacing), |
| 21 between_child_spacing_(between_child_spacing), | 50 between_child_spacing_(between_child_spacing), |
| 22 main_axis_alignment_(MAIN_AXIS_ALIGNMENT_START), | 51 main_axis_alignment_(MAIN_AXIS_ALIGNMENT_START), |
| 23 cross_axis_alignment_(CROSS_AXIS_ALIGNMENT_STRETCH), | 52 cross_axis_alignment_(CROSS_AXIS_ALIGNMENT_STRETCH), |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 40 void BoxLayout::ClearFlexForView(const View* view) { | 69 void BoxLayout::ClearFlexForView(const View* view) { |
| 41 DCHECK(view); | 70 DCHECK(view); |
| 42 flex_map_.erase(view); | 71 flex_map_.erase(view); |
| 43 } | 72 } |
| 44 | 73 |
| 45 void BoxLayout::SetDefaultFlex(int default_flex) { | 74 void BoxLayout::SetDefaultFlex(int default_flex) { |
| 46 DCHECK_GE(default_flex, 0); | 75 DCHECK_GE(default_flex, 0); |
| 47 default_flex_ = default_flex; | 76 default_flex_ = default_flex; |
| 48 } | 77 } |
| 49 | 78 |
| 79 void BoxLayout::ClearMarginsForView(const View* view) { | |
| 80 margin_map_.erase(view); | |
| 81 } | |
| 82 | |
| 83 void BoxLayout::SetViewMargins(const View* view, const gfx::Insets& margins) { | |
| 84 margin_map_[view] = margins; | |
| 85 } | |
| 86 | |
| 50 void BoxLayout::Layout(View* host) { | 87 void BoxLayout::Layout(View* host) { |
| 51 DCHECK_EQ(host_, host); | 88 DCHECK_EQ(host_, host); |
| 52 gfx::Rect child_area(host->GetLocalBounds()); | 89 gfx::Rect child_area(host->GetContentsBounds()); |
| 53 child_area.Inset(host->GetInsets()); | |
| 54 child_area.Inset(inside_border_insets_); | 90 child_area.Inset(inside_border_insets_); |
| 55 | 91 |
| 56 int total_main_axis_size = 0; | 92 int total_main_axis_size = 0; |
| 57 int num_visible = 0; | 93 int num_visible = 0; |
| 58 int flex_sum = 0; | 94 int flex_sum = 0; |
| 59 // Calculate the total size of children in the main axis. | 95 // Calculate the total size of children in the main axis. |
| 60 for (int i = 0; i < host->child_count(); ++i) { | 96 for (int i = 0; i < host->child_count(); ++i) { |
| 61 View* child = host->child_at(i); | 97 ViewWrapper child = ViewWrapper(this, host->child_at(i)); |
| 62 if (!child->visible()) | 98 if (!child.visible()) |
| 63 continue; | 99 continue; |
| 64 int flex = GetFlexForView(child); | 100 int flex = GetFlexForView(child.view()); |
| 65 int child_main_axis_size = MainAxisSizeForView(child, child_area.width()); | 101 int child_main_axis_size = |
| 102 MainAxisSizeForView(child.view(), child_area.width()); | |
| 66 if (child_main_axis_size == 0 && flex == 0) | 103 if (child_main_axis_size == 0 && flex == 0) |
| 67 continue; | 104 continue; |
| 68 total_main_axis_size += child_main_axis_size + between_child_spacing_; | 105 total_main_axis_size += child_main_axis_size + between_child_spacing_; |
| 69 ++num_visible; | 106 ++num_visible; |
| 70 flex_sum += flex; | 107 flex_sum += flex; |
| 71 } | 108 } |
| 72 | 109 |
| 73 if (!num_visible) | 110 if (!num_visible) |
| 74 return; | 111 return; |
| 75 | 112 |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 99 gfx::Rect new_child_area(child_area); | 136 gfx::Rect new_child_area(child_area); |
| 100 SetMainAxisPosition(position, &new_child_area); | 137 SetMainAxisPosition(position, &new_child_area); |
| 101 SetMainAxisSize(size, &new_child_area); | 138 SetMainAxisSize(size, &new_child_area); |
| 102 child_area.Intersect(new_child_area); | 139 child_area.Intersect(new_child_area); |
| 103 } | 140 } |
| 104 | 141 |
| 105 int main_position = MainAxisPosition(child_area); | 142 int main_position = MainAxisPosition(child_area); |
| 106 int total_padding = 0; | 143 int total_padding = 0; |
| 107 int current_flex = 0; | 144 int current_flex = 0; |
| 108 for (int i = 0; i < host->child_count(); ++i) { | 145 for (int i = 0; i < host->child_count(); ++i) { |
| 109 View* child = host->child_at(i); | 146 ViewWrapper child = ViewWrapper(this, host->child_at(i)); |
| 110 if (!child->visible()) | 147 if (!child.visible()) |
| 111 continue; | 148 continue; |
| 112 | 149 |
| 113 // TODO(bruthig): Fix this. The main axis should be calculated before | 150 // TODO(bruthig): Fix this. The main axis should be calculated before |
| 114 // the cross axis size because child Views may calculate their cross axis | 151 // 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. | 152 // size based on their main axis size. See https://crbug.com/682266. |
| 116 | 153 |
| 117 // Calculate cross axis size. | 154 // Calculate cross axis size. |
| 118 gfx::Rect bounds(child_area); | 155 gfx::Rect bounds(child_area); |
| 119 SetMainAxisPosition(main_position, &bounds); | 156 SetMainAxisPosition(main_position, &bounds); |
| 120 if (cross_axis_alignment_ != CROSS_AXIS_ALIGNMENT_STRETCH) { | 157 if (cross_axis_alignment_ != CROSS_AXIS_ALIGNMENT_STRETCH) { |
| 121 int free_space = CrossAxisSize(bounds) - CrossAxisSizeForView(child); | 158 int free_space = |
| 159 CrossAxisSize(bounds) - CrossAxisSizeForView(child.view()); | |
| 122 int position = CrossAxisPosition(bounds); | 160 int position = CrossAxisPosition(bounds); |
| 123 if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_CENTER) { | 161 if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_CENTER) { |
| 124 position += free_space / 2; | 162 position += free_space / 2; |
| 125 } else if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_END) { | 163 } else if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_END) { |
| 126 position += free_space; | 164 position += free_space; |
| 127 } | 165 } |
| 128 SetCrossAxisPosition(position, &bounds); | 166 SetCrossAxisPosition(position, &bounds); |
| 129 SetCrossAxisSize(CrossAxisSizeForView(child), &bounds); | 167 SetCrossAxisSize(CrossAxisSizeForView(child.view()), &bounds); |
| 130 } | 168 } |
| 131 | 169 |
| 132 // Calculate flex padding. | 170 // Calculate flex padding. |
| 133 int current_padding = 0; | 171 int current_padding = 0; |
| 134 if (GetFlexForView(child) > 0) { | 172 if (GetFlexForView(child.view()) > 0) { |
| 135 current_flex += GetFlexForView(child); | 173 current_flex += GetFlexForView(child.view()); |
| 136 int quot = (main_free_space * current_flex) / flex_sum; | 174 int quot = (main_free_space * current_flex) / flex_sum; |
| 137 int rem = (main_free_space * current_flex) % flex_sum; | 175 int rem = (main_free_space * current_flex) % flex_sum; |
| 138 current_padding = quot - total_padding; | 176 current_padding = quot - total_padding; |
| 139 // Use the current remainder to round to the nearest pixel. | 177 // Use the current remainder to round to the nearest pixel. |
| 140 if (std::abs(rem) * 2 >= flex_sum) | 178 if (std::abs(rem) * 2 >= flex_sum) |
| 141 current_padding += main_free_space > 0 ? 1 : -1; | 179 current_padding += main_free_space > 0 ? 1 : -1; |
| 142 total_padding += current_padding; | 180 total_padding += current_padding; |
| 143 } | 181 } |
| 144 | 182 |
| 145 // Set main axis size. | 183 // Set main axis size. |
| 146 // TODO(bruthig): Use the allocated width to determine the cross axis size. | 184 // TODO(bruthig): Use the allocated width to determine the cross axis size. |
| 147 // See https://crbug.com/682266. | 185 // See https://crbug.com/682266. |
| 148 int child_main_axis_size = MainAxisSizeForView(child, child_area.width()); | 186 int child_main_axis_size = |
| 187 MainAxisSizeForView(child.view(), child_area.width()); | |
| 149 SetMainAxisSize(child_main_axis_size + current_padding, &bounds); | 188 SetMainAxisSize(child_main_axis_size + current_padding, &bounds); |
| 150 if (MainAxisSize(bounds) > 0 || GetFlexForView(child) > 0) | 189 if (MainAxisSize(bounds) > 0 || GetFlexForView(child.view()) > 0) |
| 151 main_position += MainAxisSize(bounds) + between_child_spacing_; | 190 main_position += MainAxisSize(bounds) + between_child_spacing_; |
| 152 | 191 |
| 153 // Clamp child view bounds to |child_area|. | 192 // Clamp child view bounds to |child_area|. |
| 154 bounds.Intersect(child_area); | 193 bounds.Intersect(child_area); |
| 155 child->SetBoundsRect(bounds); | 194 child.SetBoundsRect(bounds); |
| 156 } | 195 } |
| 157 | 196 |
| 158 // Flex views should have grown/shrunk to consume all free space. | 197 // Flex views should have grown/shrunk to consume all free space. |
| 159 if (flex_sum) | 198 if (flex_sum) |
| 160 DCHECK_EQ(total_padding, main_free_space); | 199 DCHECK_EQ(total_padding, main_free_space); |
| 161 } | 200 } |
| 162 | 201 |
| 163 gfx::Size BoxLayout::GetPreferredSize(const View* host) const { | 202 gfx::Size BoxLayout::GetPreferredSize(const View* host) const { |
| 164 DCHECK_EQ(host_, host); | 203 DCHECK_EQ(host_, host); |
| 165 // Calculate the child views' preferred width. | 204 // Calculate the child views' preferred width. |
| 166 int width = 0; | 205 int width = 0; |
| 167 if (orientation_ == kVertical) { | 206 if (orientation_ == kVertical) { |
| 168 for (int i = 0; i < host->child_count(); ++i) { | 207 for (int i = 0; i < host->child_count(); ++i) { |
| 169 const View* child = host->child_at(i); | 208 const ViewWrapper child = ViewWrapper(this, host->child_at(i)); |
| 170 if (!child->visible()) | 209 if (!child.visible()) |
| 171 continue; | 210 continue; |
| 172 | 211 |
| 173 width = std::max(width, child->GetPreferredSize().width()); | 212 width = std::max(width, child.GetPreferredSize().width()); |
| 174 } | 213 } |
| 175 width = std::max(width, minimum_cross_axis_size_); | 214 width = std::max(width, minimum_cross_axis_size_); |
| 176 } | 215 } |
| 177 | 216 |
| 178 return GetPreferredSizeForChildWidth(host, width); | 217 return GetPreferredSizeForChildWidth(host, width); |
| 179 } | 218 } |
| 180 | 219 |
| 181 int BoxLayout::GetPreferredHeightForWidth(const View* host, int width) const { | 220 int BoxLayout::GetPreferredHeightForWidth(const View* host, int width) const { |
| 182 DCHECK_EQ(host_, host); | 221 DCHECK_EQ(host_, host); |
| 183 int child_width = width - NonChildSize(host).width(); | 222 int child_width = width - NonChildSize(host).width(); |
| 184 return GetPreferredSizeForChildWidth(host, child_width).height(); | 223 return GetPreferredSizeForChildWidth(host, child_width).height(); |
| 185 } | 224 } |
| 186 | 225 |
| 187 void BoxLayout::Installed(View* host) { | 226 void BoxLayout::Installed(View* host) { |
| 188 DCHECK(!host_); | 227 DCHECK(!host_); |
| 189 host_ = host; | 228 host_ = host; |
| 190 } | 229 } |
| 191 | 230 |
| 192 void BoxLayout::ViewRemoved(View* host, View* view) { | 231 void BoxLayout::ViewRemoved(View* host, View* view) { |
| 193 ClearFlexForView(view); | 232 ClearFlexForView(view); |
| 233 ClearMarginsForView(view); | |
| 194 } | 234 } |
| 195 | 235 |
| 196 int BoxLayout::GetFlexForView(const View* view) const { | 236 int BoxLayout::GetFlexForView(const View* view) const { |
| 197 std::map<const View*, int>::const_iterator it = flex_map_.find(view); | 237 FlexMap::const_iterator it = flex_map_.find(view); |
| 198 if (it == flex_map_.end()) | 238 if (it == flex_map_.end()) |
| 199 return default_flex_; | 239 return default_flex_; |
| 200 | 240 |
| 201 return it->second; | 241 return it->second; |
| 202 } | 242 } |
| 203 | 243 |
| 204 int BoxLayout::MainAxisSize(const gfx::Rect& rect) const { | 244 int BoxLayout::MainAxisSize(const gfx::Rect& rect) const { |
| 205 return orientation_ == kHorizontal ? rect.width() : rect.height(); | 245 return orientation_ == kHorizontal ? rect.width() : rect.height(); |
| 206 } | 246 } |
| 207 | 247 |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 256 } | 296 } |
| 257 | 297 |
| 258 int BoxLayout::CrossAxisSizeForView(const View* view) const { | 298 int BoxLayout::CrossAxisSizeForView(const View* view) const { |
| 259 // TODO(bruthig): For horizontal case use the available width and not the | 299 // TODO(bruthig): For horizontal case use the available width and not the |
| 260 // preferred width. See https://crbug.com/682266. | 300 // preferred width. See https://crbug.com/682266. |
| 261 return orientation_ == kVertical | 301 return orientation_ == kVertical |
| 262 ? view->GetPreferredSize().width() | 302 ? view->GetPreferredSize().width() |
| 263 : view->GetHeightForWidth(view->GetPreferredSize().width()); | 303 : view->GetHeightForWidth(view->GetPreferredSize().width()); |
| 264 } | 304 } |
| 265 | 305 |
| 266 gfx::Size BoxLayout::GetPreferredSizeForChildWidth(const View* host, | 306 gfx::Size BoxLayout::GetPreferredSizeForChildWidth(const View* host, |
|
sky
2017/04/25 22:46:07
It seems like what you're doing here is getting th
kylix_rd
2017/04/26 17:41:33
The next patch should resolve this confusion. The
| |
| 267 int child_area_width) const { | 307 int child_area_width) const { |
| 268 gfx::Rect child_area_bounds; | 308 gfx::Rect child_area_bounds; |
| 269 | 309 |
| 270 if (orientation_ == kHorizontal) { | 310 if (orientation_ == kHorizontal) { |
| 271 // Horizontal layouts ignore |child_area_width|, meaning they mimic the | 311 // Horizontal layouts ignore |child_area_width|, meaning they mimic the |
| 272 // default behavior of GridLayout::GetPreferredHeightForWidth(). | 312 // default behavior of GridLayout::GetPreferredHeightForWidth(). |
| 273 // TODO(estade|bruthig): Fix this See // https://crbug.com/682266. | 313 // TODO(estade|bruthig): Fix this See // https://crbug.com/682266. |
| 274 int position = 0; | 314 int position = 0; |
| 275 for (int i = 0; i < host->child_count(); ++i) { | 315 for (int i = 0; i < host->child_count(); ++i) { |
| 276 const View* child = host->child_at(i); | 316 const ViewWrapper child = ViewWrapper(this, host->child_at(i)); |
| 277 if (!child->visible()) | 317 if (!child.visible()) |
| 278 continue; | 318 continue; |
| 279 | 319 |
| 280 gfx::Size size(child->GetPreferredSize()); | 320 gfx::Size size(child.GetPreferredSize()); |
| 281 if (size.IsEmpty()) | 321 if (size.IsEmpty()) |
| 282 continue; | 322 continue; |
| 283 | 323 |
| 284 gfx::Rect child_bounds(position, 0, size.width(), size.height()); | 324 gfx::Rect child_bounds(position, 0, size.width(), size.height()); |
| 285 child_area_bounds.Union(child_bounds); | 325 child_area_bounds.Union(child_bounds); |
| 286 position += size.width() + between_child_spacing_; | 326 position += size.width() + between_child_spacing_; |
| 287 } | 327 } |
| 288 child_area_bounds.set_height( | 328 child_area_bounds.set_height( |
| 289 std::max(child_area_bounds.height(), minimum_cross_axis_size_)); | 329 std::max(child_area_bounds.height(), minimum_cross_axis_size_)); |
| 290 } else { | 330 } else { |
| 291 int height = 0; | 331 int height = 0; |
| 292 for (int i = 0; i < host->child_count(); ++i) { | 332 for (int i = 0; i < host->child_count(); ++i) { |
| 293 const View* child = host->child_at(i); | 333 const ViewWrapper child = ViewWrapper(this, host->child_at(i)); |
| 294 if (!child->visible()) | 334 if (!child.visible()) |
| 295 continue; | 335 continue; |
| 296 | 336 |
| 297 // Use the child area width for getting the height if the child is | 337 // Use the child area width for getting the height if the child is |
| 298 // supposed to stretch. Use its preferred size otherwise. | 338 // supposed to stretch. Use its preferred size otherwise. |
| 299 int extra_height = MainAxisSizeForView(child, child_area_width); | 339 int extra_height = MainAxisSizeForView(child.view(), child_area_width); |
| 300 // Only add |between_child_spacing_| if this is not the only child. | 340 // Only add |between_child_spacing_| if this is not the only child. |
| 301 if (height != 0 && extra_height > 0) | 341 if (height != 0 && extra_height > 0) |
| 302 height += between_child_spacing_; | 342 height += between_child_spacing_; |
| 303 height += extra_height; | 343 height += extra_height; |
| 304 } | 344 } |
| 305 | 345 |
| 306 child_area_bounds.set_width(child_area_width); | 346 child_area_bounds.set_width(child_area_width); |
| 307 child_area_bounds.set_height(height); | 347 child_area_bounds.set_height(height); |
| 308 } | 348 } |
| 309 | 349 |
| 310 gfx::Size non_child_size = NonChildSize(host); | 350 gfx::Size non_child_size = NonChildSize(host); |
| 311 return gfx::Size(child_area_bounds.width() + non_child_size.width(), | 351 return gfx::Size(child_area_bounds.width() + non_child_size.width(), |
| 312 child_area_bounds.height() + non_child_size.height()); | 352 child_area_bounds.height() + non_child_size.height()); |
| 313 } | 353 } |
| 314 | 354 |
| 315 gfx::Size BoxLayout::NonChildSize(const View* host) const { | 355 gfx::Size BoxLayout::NonChildSize(const View* host) const { |
| 316 gfx::Insets insets(host->GetInsets()); | 356 gfx::Insets insets(host->GetInsets()); |
| 317 return gfx::Size(insets.width() + inside_border_insets_.width(), | 357 return gfx::Size(insets.width() + inside_border_insets_.width(), |
| 318 insets.height() + inside_border_insets_.height()); | 358 insets.height() + inside_border_insets_.height()); |
| 319 } | 359 } |
| 320 | 360 |
| 361 gfx::Insets BoxLayout::GetViewMargins(const View* view) const { | |
| 362 MarginMap::const_iterator pos = margin_map_.find(view); | |
| 363 if (pos != margin_map_.cend()) | |
| 364 return pos->second; | |
| 365 return gfx::Insets(); | |
| 366 } | |
| 367 | |
| 321 } // namespace views | 368 } // namespace views |
| OLD | NEW |