| 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), |
| 25 host_(NULL) { |
| 24 } | 26 } |
| 25 | 27 |
| 26 BoxLayout::~BoxLayout() { | 28 BoxLayout::~BoxLayout() { |
| 27 } | 29 } |
| 28 | 30 |
| 31 void BoxLayout::SetFlexForView(const View* view, int flex_weight) { |
| 32 DCHECK(host_); |
| 33 DCHECK(view); |
| 34 DCHECK_EQ(host_, view->parent()); |
| 35 DCHECK_GE(flex_weight, 0); |
| 36 flex_map_[view] = flex_weight; |
| 37 } |
| 38 |
| 39 void BoxLayout::ClearFlexForView(const View* view) { |
| 40 DCHECK(view); |
| 41 flex_map_.erase(view); |
| 42 } |
| 43 |
| 44 void BoxLayout::SetDefaultFlex(int default_flex) { |
| 45 DCHECK_GE(default_flex, 0); |
| 46 default_flex_ = default_flex; |
| 47 } |
| 48 |
| 29 void BoxLayout::Layout(View* host) { | 49 void BoxLayout::Layout(View* host) { |
| 50 DCHECK_EQ(host_, host); |
| 30 gfx::Rect child_area(host->GetLocalBounds()); | 51 gfx::Rect child_area(host->GetLocalBounds()); |
| 31 child_area.Inset(host->GetInsets()); | 52 child_area.Inset(host->GetInsets()); |
| 32 child_area.Inset(inside_border_insets_); | 53 child_area.Inset(inside_border_insets_); |
| 33 | 54 |
| 34 int padding = 0; | 55 int total_main_axis_size = 0; |
| 35 if (main_axis_alignment_ != MAIN_AXIS_ALIGNMENT_START) { | 56 int num_visible = 0; |
| 36 int total_main_axis_size = 0; | 57 int flex_sum = 0; |
| 37 int num_visible = 0; | 58 // Calculate the total size of children in the main axis. |
| 38 for (int i = 0; i < host->child_count(); ++i) { | 59 for (int i = 0; i < host->child_count(); ++i) { |
| 39 View* child = host->child_at(i); | 60 View* child = host->child_at(i); |
| 40 if (!child->visible()) | 61 if (!child->visible()) |
| 41 continue; | 62 continue; |
| 42 total_main_axis_size += MainAxisSizeForView(child, child_area.width()) + | 63 total_main_axis_size += |
| 43 between_child_spacing_; | 64 MainAxisSizeForView(child, child_area.width()) + between_child_spacing_; |
| 44 ++num_visible; | 65 ++num_visible; |
| 45 } | 66 flex_sum += GetFlexForView(child); |
| 67 } |
| 46 | 68 |
| 47 if (num_visible) { | 69 if (!num_visible) |
| 48 total_main_axis_size -= between_child_spacing_; | 70 return; |
| 49 int free_space = MainAxisSize(child_area) - total_main_axis_size; | 71 |
| 50 int position = MainAxisPosition(child_area); | 72 total_main_axis_size -= between_child_spacing_; |
| 51 int size = MainAxisSize(child_area); | 73 // Free space can be negative indicating that the views want to overflow. |
| 74 int main_free_space = MainAxisSize(child_area) - total_main_axis_size; |
| 75 { |
| 76 int position = MainAxisPosition(child_area); |
| 77 int size = MainAxisSize(child_area); |
| 78 if (!flex_sum) { |
| 52 switch (main_axis_alignment_) { | 79 switch (main_axis_alignment_) { |
| 53 case MAIN_AXIS_ALIGNMENT_FILL: | 80 case MAIN_AXIS_ALIGNMENT_START: |
| 54 padding = std::max(free_space / num_visible, 0); | |
| 55 break; | 81 break; |
| 56 case MAIN_AXIS_ALIGNMENT_CENTER: | 82 case MAIN_AXIS_ALIGNMENT_CENTER: |
| 57 position += free_space / 2; | 83 position += main_free_space / 2; |
| 58 size = total_main_axis_size; | 84 size = total_main_axis_size; |
| 59 break; | 85 break; |
| 60 case MAIN_AXIS_ALIGNMENT_END: | 86 case MAIN_AXIS_ALIGNMENT_END: |
| 61 position += free_space; | 87 position += main_free_space; |
| 62 size = total_main_axis_size; | 88 size = total_main_axis_size; |
| 63 break; | 89 break; |
| 64 default: | 90 default: |
| 65 NOTREACHED(); | 91 NOTREACHED(); |
| 66 break; | 92 break; |
| 67 } | 93 } |
| 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 } | 94 } |
| 95 gfx::Rect new_child_area(child_area); |
| 96 SetMainAxisPosition(position, &new_child_area); |
| 97 SetMainAxisSize(size, &new_child_area); |
| 98 child_area.Intersect(new_child_area); |
| 73 } | 99 } |
| 74 | 100 |
| 75 int main_position = MainAxisPosition(child_area); | 101 int main_position = MainAxisPosition(child_area); |
| 102 int total_padding = 0; |
| 103 int current_flex = 0; |
| 76 for (int i = 0; i < host->child_count(); ++i) { | 104 for (int i = 0; i < host->child_count(); ++i) { |
| 77 View* child = host->child_at(i); | 105 View* child = host->child_at(i); |
| 78 if (child->visible()) { | 106 if (!child->visible()) |
| 79 gfx::Rect bounds(child_area); | 107 continue; |
| 80 SetMainAxisPosition(main_position, &bounds); | 108 |
| 81 if (cross_axis_alignment_ != CROSS_AXIS_ALIGNMENT_STRETCH) { | 109 // Calculate cross axis size. |
| 82 int free_space = CrossAxisSize(bounds) - CrossAxisSizeForView(child); | 110 gfx::Rect bounds(child_area); |
| 83 int position = CrossAxisPosition(bounds); | 111 SetMainAxisPosition(main_position, &bounds); |
| 84 if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_CENTER) { | 112 if (cross_axis_alignment_ != CROSS_AXIS_ALIGNMENT_STRETCH) { |
| 85 position += free_space / 2; | 113 int free_space = CrossAxisSize(bounds) - CrossAxisSizeForView(child); |
| 86 } else if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_END) { | 114 int position = CrossAxisPosition(bounds); |
| 87 position += free_space; | 115 if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_CENTER) { |
| 88 } | 116 position += free_space / 2; |
| 89 SetCrossAxisPosition(position, &bounds); | 117 } else if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_END) { |
| 90 SetCrossAxisSize(CrossAxisSizeForView(child), &bounds); | 118 position += free_space; |
| 91 } | 119 } |
| 92 int child_main_axis_size = MainAxisSizeForView(child, child_area.width()); | 120 SetCrossAxisPosition(position, &bounds); |
| 93 SetMainAxisSize(child_main_axis_size + padding, &bounds); | 121 SetCrossAxisSize(CrossAxisSizeForView(child), &bounds); |
| 94 if (MainAxisSize(bounds) > 0) | 122 } |
| 95 main_position += MainAxisSize(bounds) + between_child_spacing_; | |
| 96 | 123 |
| 97 // Clamp child view bounds to |child_area|. | 124 // Calculate flex padding. |
| 98 bounds.Intersect(child_area); | 125 int current_padding = 0; |
| 99 child->SetBoundsRect(bounds); | 126 if (GetFlexForView(child) > 0) { |
| 127 current_flex += GetFlexForView(child); |
| 128 int quot = (main_free_space * current_flex) / flex_sum; |
| 129 int rem = (main_free_space * current_flex) % flex_sum; |
| 130 current_padding = quot - total_padding; |
| 131 // Use the current remainder to round to the nearest pixel. |
| 132 if (std::abs(rem) * 2 >= flex_sum) |
| 133 current_padding += main_free_space > 0 ? 1 : -1; |
| 134 total_padding += current_padding; |
| 100 } | 135 } |
| 136 |
| 137 // Set main axis size. |
| 138 int child_main_axis_size = MainAxisSizeForView(child, child_area.width()); |
| 139 SetMainAxisSize(child_main_axis_size + current_padding, &bounds); |
| 140 if (MainAxisSize(bounds) > 0 || GetFlexForView(child) > 0) |
| 141 main_position += MainAxisSize(bounds) + between_child_spacing_; |
| 142 |
| 143 // Clamp child view bounds to |child_area|. |
| 144 bounds.Intersect(child_area); |
| 145 child->SetBoundsRect(bounds); |
| 101 } | 146 } |
| 147 |
| 148 // Flex views should have grown/shrunk to consume all free space. |
| 149 if (flex_sum) |
| 150 DCHECK_EQ(total_padding, main_free_space); |
| 102 } | 151 } |
| 103 | 152 |
| 104 gfx::Size BoxLayout::GetPreferredSize(const View* host) const { | 153 gfx::Size BoxLayout::GetPreferredSize(const View* host) const { |
| 154 DCHECK_EQ(host_, host); |
| 105 // Calculate the child views' preferred width. | 155 // Calculate the child views' preferred width. |
| 106 int width = 0; | 156 int width = 0; |
| 107 if (orientation_ == kVertical) { | 157 if (orientation_ == kVertical) { |
| 108 for (int i = 0; i < host->child_count(); ++i) { | 158 for (int i = 0; i < host->child_count(); ++i) { |
| 109 const View* child = host->child_at(i); | 159 const View* child = host->child_at(i); |
| 110 if (!child->visible()) | 160 if (!child->visible()) |
| 111 continue; | 161 continue; |
| 112 | 162 |
| 113 width = std::max(width, child->GetPreferredSize().width()); | 163 width = std::max(width, child->GetPreferredSize().width()); |
| 114 } | 164 } |
| 115 } | 165 } |
| 116 | 166 |
| 117 return GetPreferredSizeForChildWidth(host, width); | 167 return GetPreferredSizeForChildWidth(host, width); |
| 118 } | 168 } |
| 119 | 169 |
| 120 int BoxLayout::GetPreferredHeightForWidth(const View* host, int width) const { | 170 int BoxLayout::GetPreferredHeightForWidth(const View* host, int width) const { |
| 171 DCHECK_EQ(host_, host); |
| 121 int child_width = width - NonChildSize(host).width(); | 172 int child_width = width - NonChildSize(host).width(); |
| 122 return GetPreferredSizeForChildWidth(host, child_width).height(); | 173 return GetPreferredSizeForChildWidth(host, child_width).height(); |
| 123 } | 174 } |
| 124 | 175 |
| 176 void BoxLayout::Installed(View* host) { |
| 177 DCHECK(!host_); |
| 178 host_ = host; |
| 179 } |
| 180 |
| 181 void BoxLayout::Uninstalled(View* host) { |
| 182 DCHECK_EQ(host_, host); |
| 183 host_ = NULL; |
| 184 flex_map_.clear(); |
| 185 } |
| 186 |
| 187 void BoxLayout::ViewRemoved(View* host, View* view) { |
| 188 ClearFlexForView(view); |
| 189 } |
| 190 |
| 191 int BoxLayout::GetFlexForView(const View* view) const { |
| 192 std::map<const View*, int>::const_iterator it = flex_map_.find(view); |
| 193 if (it == flex_map_.end()) |
| 194 return default_flex_; |
| 195 |
| 196 return it->second; |
| 197 } |
| 198 |
| 125 int BoxLayout::MainAxisSize(const gfx::Rect& rect) const { | 199 int BoxLayout::MainAxisSize(const gfx::Rect& rect) const { |
| 126 return orientation_ == kHorizontal ? rect.width() : rect.height(); | 200 return orientation_ == kHorizontal ? rect.width() : rect.height(); |
| 127 } | 201 } |
| 128 | 202 |
| 129 int BoxLayout::MainAxisPosition(const gfx::Rect& rect) const { | 203 int BoxLayout::MainAxisPosition(const gfx::Rect& rect) const { |
| 130 return orientation_ == kHorizontal ? rect.x() : rect.y(); | 204 return orientation_ == kHorizontal ? rect.x() : rect.y(); |
| 131 } | 205 } |
| 132 | 206 |
| 133 void BoxLayout::SetMainAxisSize(int size, gfx::Rect* rect) const { | 207 void BoxLayout::SetMainAxisSize(int size, gfx::Rect* rect) const { |
| 134 if (orientation_ == kHorizontal) | 208 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()); | 303 child_area_bounds.height() + non_child_size.height()); |
| 230 } | 304 } |
| 231 | 305 |
| 232 gfx::Size BoxLayout::NonChildSize(const View* host) const { | 306 gfx::Size BoxLayout::NonChildSize(const View* host) const { |
| 233 gfx::Insets insets(host->GetInsets()); | 307 gfx::Insets insets(host->GetInsets()); |
| 234 return gfx::Size(insets.width() + inside_border_insets_.width(), | 308 return gfx::Size(insets.width() + inside_border_insets_.width(), |
| 235 insets.height() + inside_border_insets_.height()); | 309 insets.height() + inside_border_insets_.height()); |
| 236 } | 310 } |
| 237 | 311 |
| 238 } // namespace views | 312 } // namespace views |
| OLD | NEW |