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

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

Issue 2836313002: BoxLayout now suports per-view margins. (Closed)
Patch Set: Created 3 years, 7 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
« ui/views/layout/box_layout.h ('K') | « ui/views/layout/box_layout.h ('k') | no next file » | 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 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
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
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
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
OLDNEW
« ui/views/layout/box_layout.h ('K') | « ui/views/layout/box_layout.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698