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 "ui/gfx/rect.h" | 7 #include "ui/gfx/rect.h" |
8 #include "ui/views/view.h" | 8 #include "ui/views/view.h" |
9 | 9 |
10 namespace views { | 10 namespace views { |
11 | 11 |
12 BoxLayout::BoxLayout(BoxLayout::Orientation orientation, | 12 BoxLayout::BoxLayout(BoxLayout::Orientation orientation, |
13 int inside_border_horizontal_spacing, | 13 int inside_border_horizontal_spacing, |
14 int inside_border_vertical_spacing, | 14 int inside_border_vertical_spacing, |
15 int between_child_spacing) | 15 int between_child_spacing) |
16 : orientation_(orientation), | 16 : orientation_(orientation), |
17 inside_border_insets_(inside_border_vertical_spacing, | 17 inside_border_insets_(inside_border_vertical_spacing, |
18 inside_border_horizontal_spacing, | 18 inside_border_horizontal_spacing, |
19 inside_border_vertical_spacing, | 19 inside_border_vertical_spacing, |
20 inside_border_horizontal_spacing), | 20 inside_border_horizontal_spacing), |
21 between_child_spacing_(between_child_spacing), | 21 between_child_spacing_(between_child_spacing), |
22 main_axis_alignment_(MAIN_AXIS_ALIGNMENT_START), | 22 main_axis_alignment_(MAIN_AXIS_ALIGNMENT_START), |
23 cross_axis_alignment_(CROSS_AXIS_ALIGNMENT_STRETCH) { | 23 cross_axis_alignment_(CROSS_AXIS_ALIGNMENT_STRETCH), |
24 default_flex_(0) { | |
24 } | 25 } |
25 | 26 |
26 BoxLayout::~BoxLayout() { | 27 BoxLayout::~BoxLayout() { |
27 } | 28 } |
28 | 29 |
30 void BoxLayout::SetFlexForViewAt(int index, int flex_weight) { | |
31 DCHECK(index >= 0); | |
32 DCHECK(flex_weight >= 0); | |
33 flex_map_[index] = flex_weight; | |
34 } | |
35 | |
36 void BoxLayout::ClearFlexForViewAt(int index) { | |
37 DCHECK(index >= 0); | |
38 flex_map_.erase(index); | |
39 } | |
40 | |
41 void BoxLayout::SetDefaultFlex(int default_flex) { | |
42 DCHECK(default_flex >= 0); | |
43 default_flex_ = default_flex; | |
44 } | |
45 | |
29 void BoxLayout::Layout(View* host) { | 46 void BoxLayout::Layout(View* host) { |
30 gfx::Rect child_area(host->GetLocalBounds()); | 47 gfx::Rect child_area(host->GetLocalBounds()); |
31 child_area.Inset(host->GetInsets()); | 48 child_area.Inset(host->GetInsets()); |
32 child_area.Inset(inside_border_insets_); | 49 child_area.Inset(inside_border_insets_); |
33 | 50 |
34 int padding = 0; | 51 int main_free_space = 0; |
benwells
2014/07/03 07:29:59
Nit: move this to just before it is used (line 65
calamity
2014/07/03 08:31:24
Done.
| |
35 if (main_axis_alignment_ != MAIN_AXIS_ALIGNMENT_START) { | 52 int total_main_axis_size = 0; |
36 int total_main_axis_size = 0; | 53 int num_visible = 0; |
37 int num_visible = 0; | 54 int flex_sum = 0; |
38 for (int i = 0; i < host->child_count(); ++i) { | 55 for (int i = 0; i < host->child_count(); ++i) { |
39 View* child = host->child_at(i); | 56 View* child = host->child_at(i); |
40 if (!child->visible()) | 57 if (!child->visible()) |
41 continue; | 58 continue; |
42 total_main_axis_size += MainAxisSizeForView(child, child_area.width()) + | 59 total_main_axis_size += |
43 between_child_spacing_; | 60 MainAxisSizeForView(child, child_area.width()) + between_child_spacing_; |
44 ++num_visible; | 61 ++num_visible; |
45 } | 62 flex_sum += GetFlexForViewAt(i); |
63 } | |
46 | 64 |
47 if (num_visible) { | 65 if (num_visible) { |
benwells
2014/07/03 07:29:59
Can you return here if !num_visible?
calamity
2014/07/03 08:31:24
Done.
| |
48 total_main_axis_size -= between_child_spacing_; | 66 total_main_axis_size -= between_child_spacing_; |
49 int free_space = MainAxisSize(child_area) - total_main_axis_size; | 67 // Free space can be negative indicating that the views want to overflow. |
50 int position = MainAxisPosition(child_area); | 68 main_free_space = MainAxisSize(child_area) - total_main_axis_size; |
51 int size = MainAxisSize(child_area); | 69 int position = MainAxisPosition(child_area); |
70 int size = MainAxisSize(child_area); | |
71 if (!flex_sum) { | |
52 switch (main_axis_alignment_) { | 72 switch (main_axis_alignment_) { |
53 case MAIN_AXIS_ALIGNMENT_FILL: | 73 case MAIN_AXIS_ALIGNMENT_START: |
54 padding = std::max(free_space / num_visible, 0); | |
55 break; | 74 break; |
56 case MAIN_AXIS_ALIGNMENT_CENTER: | 75 case MAIN_AXIS_ALIGNMENT_CENTER: |
57 position += free_space / 2; | 76 position += main_free_space / 2; |
58 size = total_main_axis_size; | 77 size = total_main_axis_size; |
59 break; | 78 break; |
60 case MAIN_AXIS_ALIGNMENT_END: | 79 case MAIN_AXIS_ALIGNMENT_END: |
61 position += free_space; | 80 position += main_free_space; |
62 size = total_main_axis_size; | 81 size = total_main_axis_size; |
63 break; | 82 break; |
64 default: | 83 default: |
65 NOTREACHED(); | 84 NOTREACHED(); |
66 break; | 85 break; |
67 } | 86 } |
68 gfx::Rect new_child_area(child_area); | |
69 SetMainAxisPosition(position, &new_child_area); | |
70 SetMainAxisSize(size, &new_child_area); | |
71 child_area.Intersect(new_child_area); | |
72 } | 87 } |
88 gfx::Rect new_child_area(child_area); | |
89 SetMainAxisPosition(position, &new_child_area); | |
90 SetMainAxisSize(size, &new_child_area); | |
91 child_area.Intersect(new_child_area); | |
73 } | 92 } |
74 | 93 |
75 int main_position = MainAxisPosition(child_area); | 94 int main_position = MainAxisPosition(child_area); |
95 int current_remainder = 0; | |
76 for (int i = 0; i < host->child_count(); ++i) { | 96 for (int i = 0; i < host->child_count(); ++i) { |
77 View* child = host->child_at(i); | 97 View* child = host->child_at(i); |
78 if (child->visible()) { | 98 if (child->visible()) { |
79 gfx::Rect bounds(child_area); | 99 gfx::Rect bounds(child_area); |
80 SetMainAxisPosition(main_position, &bounds); | 100 SetMainAxisPosition(main_position, &bounds); |
81 if (cross_axis_alignment_ != CROSS_AXIS_ALIGNMENT_STRETCH) { | 101 if (cross_axis_alignment_ != CROSS_AXIS_ALIGNMENT_STRETCH) { |
82 int free_space = CrossAxisSize(bounds) - CrossAxisSizeForView(child); | 102 int free_space = CrossAxisSize(bounds) - CrossAxisSizeForView(child); |
83 int position = CrossAxisPosition(bounds); | 103 int position = CrossAxisPosition(bounds); |
84 if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_CENTER) { | 104 if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_CENTER) { |
85 position += free_space / 2; | 105 position += free_space / 2; |
86 } else if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_END) { | 106 } else if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_END) { |
87 position += free_space; | 107 position += free_space; |
88 } | 108 } |
89 SetCrossAxisPosition(position, &bounds); | 109 SetCrossAxisPosition(position, &bounds); |
90 SetCrossAxisSize(CrossAxisSizeForView(child), &bounds); | 110 SetCrossAxisSize(CrossAxisSizeForView(child), &bounds); |
91 } | 111 } |
112 | |
113 int current_padding = 0; | |
114 if (GetFlexForViewAt(i) > 0) { | |
115 current_padding = main_free_space * GetFlexForViewAt(i) / flex_sum; | |
116 current_remainder += main_free_space * GetFlexForViewAt(i); | |
117 current_remainder %= flex_sum; | |
118 // Use the current remainder to round to the nearest pixel. | |
119 if (std::abs(current_remainder) * 2 >= flex_sum) | |
benwells
2014/07/03 07:29:59
It feels magical that this works and doesn't ever
| |
120 current_padding += main_free_space > 0 ? 1 : -1; | |
121 } | |
122 | |
92 int child_main_axis_size = MainAxisSizeForView(child, child_area.width()); | 123 int child_main_axis_size = MainAxisSizeForView(child, child_area.width()); |
93 SetMainAxisSize(child_main_axis_size + padding, &bounds); | 124 SetMainAxisSize(child_main_axis_size + current_padding, &bounds); |
94 if (MainAxisSize(bounds) > 0) | 125 if (MainAxisSize(bounds) > 0 || GetFlexForViewAt(i) > 0) |
95 main_position += MainAxisSize(bounds) + between_child_spacing_; | 126 main_position += MainAxisSize(bounds) + between_child_spacing_; |
96 | 127 |
97 // Clamp child view bounds to |child_area|. | 128 // Clamp child view bounds to |child_area|. |
98 bounds.Intersect(child_area); | 129 bounds.Intersect(child_area); |
99 child->SetBoundsRect(bounds); | 130 child->SetBoundsRect(bounds); |
100 } | 131 } |
101 } | 132 } |
133 DCHECK_EQ(0, current_remainder); | |
102 } | 134 } |
103 | 135 |
104 gfx::Size BoxLayout::GetPreferredSize(const View* host) const { | 136 gfx::Size BoxLayout::GetPreferredSize(const View* host) const { |
105 // Calculate the child views' preferred width. | 137 // Calculate the child views' preferred width. |
106 int width = 0; | 138 int width = 0; |
107 if (orientation_ == kVertical) { | 139 if (orientation_ == kVertical) { |
108 for (int i = 0; i < host->child_count(); ++i) { | 140 for (int i = 0; i < host->child_count(); ++i) { |
109 const View* child = host->child_at(i); | 141 const View* child = host->child_at(i); |
110 if (!child->visible()) | 142 if (!child->visible()) |
111 continue; | 143 continue; |
112 | 144 |
113 width = std::max(width, child->GetPreferredSize().width()); | 145 width = std::max(width, child->GetPreferredSize().width()); |
114 } | 146 } |
115 } | 147 } |
116 | 148 |
117 return GetPreferredSizeForChildWidth(host, width); | 149 return GetPreferredSizeForChildWidth(host, width); |
118 } | 150 } |
119 | 151 |
120 int BoxLayout::GetPreferredHeightForWidth(const View* host, int width) const { | 152 int BoxLayout::GetPreferredHeightForWidth(const View* host, int width) const { |
121 int child_width = width - NonChildSize(host).width(); | 153 int child_width = width - NonChildSize(host).width(); |
122 return GetPreferredSizeForChildWidth(host, child_width).height(); | 154 return GetPreferredSizeForChildWidth(host, child_width).height(); |
123 } | 155 } |
124 | 156 |
157 int BoxLayout::GetFlexForViewAt(int index) { | |
158 std::map<int, int>::const_iterator it = flex_map_.find(index); | |
159 if (it == flex_map_.end()) | |
160 return default_flex_; | |
161 | |
162 return it->second; | |
163 } | |
164 | |
125 int BoxLayout::MainAxisSize(const gfx::Rect& rect) const { | 165 int BoxLayout::MainAxisSize(const gfx::Rect& rect) const { |
126 return orientation_ == kHorizontal ? rect.width() : rect.height(); | 166 return orientation_ == kHorizontal ? rect.width() : rect.height(); |
127 } | 167 } |
128 | 168 |
129 int BoxLayout::MainAxisPosition(const gfx::Rect& rect) const { | 169 int BoxLayout::MainAxisPosition(const gfx::Rect& rect) const { |
130 return orientation_ == kHorizontal ? rect.x() : rect.y(); | 170 return orientation_ == kHorizontal ? rect.x() : rect.y(); |
131 } | 171 } |
132 | 172 |
133 void BoxLayout::SetMainAxisSize(int size, gfx::Rect* rect) const { | 173 void BoxLayout::SetMainAxisSize(int size, gfx::Rect* rect) const { |
134 if (orientation_ == kHorizontal) | 174 if (orientation_ == kHorizontal) |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
229 child_area_bounds.height() + non_child_size.height()); | 269 child_area_bounds.height() + non_child_size.height()); |
230 } | 270 } |
231 | 271 |
232 gfx::Size BoxLayout::NonChildSize(const View* host) const { | 272 gfx::Size BoxLayout::NonChildSize(const View* host) const { |
233 gfx::Insets insets(host->GetInsets()); | 273 gfx::Insets insets(host->GetInsets()); |
234 return gfx::Size(insets.width() + inside_border_insets_.width(), | 274 return gfx::Size(insets.width() + inside_border_insets_.width(), |
235 insets.height() + inside_border_insets_.height()); | 275 insets.height() + inside_border_insets_.height()); |
236 } | 276 } |
237 | 277 |
238 } // namespace views | 278 } // namespace views |
OLD | NEW |