OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "ui/views/layout/align_layout_state.h" |
| 6 |
| 7 #include "base/logging.h" |
| 8 |
| 9 namespace views { |
| 10 |
| 11 // This special value represents the absence of the align attribute. |
| 12 static constexpr Align Align_None = |
| 13 static_cast<Align>(static_cast<int>(Align::Content) + 1); |
| 14 |
| 15 AlignLayoutState::AnchorState::AnchorState() |
| 16 : anchors({Anchor::Top, Anchor::Left}), anchor_basis(0, 0) {} |
| 17 |
| 18 AlignLayoutState::AnchorState::AnchorState(Anchors anchors) |
| 19 : anchors(anchors), anchor_basis(0, 0) {} |
| 20 |
| 21 AlignLayoutState::AnchorState::AnchorState(const AnchorState& state) |
| 22 : anchors(state.anchors), anchor_basis(state.anchor_basis) {} |
| 23 |
| 24 AlignLayoutState::AnchorState::~AnchorState() {} |
| 25 |
| 26 AlignLayoutState::AlignLayoutState() {} |
| 27 |
| 28 AlignLayoutState::~AlignLayoutState() {} |
| 29 |
| 30 void AlignLayoutState::AlignView(View* view, Align align) { |
| 31 AlignMap::iterator pos = align_map_.find(view); |
| 32 if (pos == align_map_.end()) |
| 33 align_map_.emplace(std::make_pair(view, align)); |
| 34 else |
| 35 pos->second = align; |
| 36 UpdateAnchorAlign(view, align); |
| 37 } |
| 38 |
| 39 void AlignLayoutState::AnchorView(View* view, Anchors anchors) { |
| 40 AnchorMap::iterator pos = anchor_map_.find(view); |
| 41 if (pos == anchor_map_.end()) |
| 42 pos = anchor_map_.emplace(std::make_pair(view, AnchorState(anchors))).first; |
| 43 else |
| 44 pos->second.anchors = anchors; |
| 45 UpdateAnchorBasis(view); |
| 46 } |
| 47 |
| 48 void AlignLayoutState::AnchorView( |
| 49 View* view, |
| 50 const std::initializer_list<Anchor>& anchors) { |
| 51 AnchorView(view, Anchors(anchors)); |
| 52 } |
| 53 |
| 54 void AlignLayoutState::AlignViews(View* host, |
| 55 views::Align align, |
| 56 gfx::Rect& contents) { |
| 57 View::Views align_list; |
| 58 for (int i = 0; i < host->child_count(); ++i) { |
| 59 Align child_align; |
| 60 View* child = host->child_at(i); |
| 61 if (child->visible() && FindAlign(child, &child_align) |
| 62 ? child_align == align |
| 63 : align == Align_None) { |
| 64 View::Views::iterator j = align_list.begin(); |
| 65 while (j < align_list.end() && !ShouldInsert(child, *j, align)) |
| 66 j++; |
| 67 align_list.insert(j, child); |
| 68 } |
| 69 } |
| 70 for (View* child : align_list) { |
| 71 PlaceView(child, align, contents); |
| 72 UpdateAnchorBasis(child); |
| 73 } |
| 74 } |
| 75 |
| 76 bool AlignLayoutState::FindAlign(View* view, Align* align) { |
| 77 AlignMap::iterator pos = align_map_.find(view); |
| 78 if (pos != align_map_.end()) { |
| 79 if (align) |
| 80 *align = pos->second; |
| 81 return true; |
| 82 } |
| 83 return false; |
| 84 } |
| 85 |
| 86 bool AlignLayoutState::FindAlign(const View* view, Align* align) const { |
| 87 return const_cast<AlignLayoutState*>(this)->FindAlign(const_cast<View*>(view), |
| 88 align); |
| 89 } |
| 90 |
| 91 bool AlignLayoutState::FindAnchorState(View* view, AnchorState** state) { |
| 92 AnchorMap::iterator pos = anchor_map_.find(view); |
| 93 if (pos != anchor_map_.end()) { |
| 94 if (state) |
| 95 *state = &pos->second; |
| 96 return true; |
| 97 } |
| 98 return false; |
| 99 } |
| 100 |
| 101 void AlignLayoutState::InternalUpdateAnchorBasis(View* view, |
| 102 AnchorMap::iterator pos) { |
| 103 AnchorState& state = pos->second; |
| 104 gfx::Point center_point = view->bounds().CenterPoint(); |
| 105 if (state.anchors.Has(Anchor::Right)) |
| 106 if (state.anchors.Has(Anchor::Left)) |
| 107 state.anchor_basis.set_x(view->width()); |
| 108 else |
| 109 state.anchor_basis.set_x(view->x()); |
| 110 else |
| 111 state.anchor_basis.set_x(center_point.x()); |
| 112 if (state.anchors.Has(Anchor::Bottom)) |
| 113 if (state.anchors.Has(Anchor::Top)) |
| 114 state.anchor_basis.set_y(view->height()); |
| 115 else |
| 116 state.anchor_basis.set_y(view->y()); |
| 117 else |
| 118 state.anchor_basis.set_y(center_point.y()); |
| 119 } |
| 120 |
| 121 void AlignLayoutState::Installed(View* host) { |
| 122 last_host_size_ = host->size(); |
| 123 } |
| 124 |
| 125 void AlignLayoutState::Layout(View* host) { |
| 126 static const Align alignstyle[] = { |
| 127 views::Align::Top, views::Align::Bottom, views::Align::Left, |
| 128 views::Align::Right, views::Align::Content, Align_None}; |
| 129 if (ShouldAlign(host)) { |
| 130 gfx::Rect contents = host->GetContentsBounds(); |
| 131 for (std::size_t i = 0; i < sizeof(alignstyle) / sizeof(alignstyle[0]); i++) |
| 132 AlignViews(host, alignstyle[i], contents); |
| 133 last_host_size_ = host->size(); |
| 134 } |
| 135 } |
| 136 |
| 137 void AlignLayoutState::NoAlignView(View* view) { |
| 138 AlignMap::iterator pos = align_map_.find(view); |
| 139 if (pos != align_map_.end()) |
| 140 align_map_.erase(pos); |
| 141 } |
| 142 |
| 143 void AlignLayoutState::NoAnchorView(View* view) { |
| 144 AnchorMap::iterator pos = anchor_map_.find(view); |
| 145 if (pos != anchor_map_.end()) |
| 146 anchor_map_.erase(pos); |
| 147 } |
| 148 |
| 149 gfx::Size AlignLayoutState::GetPreferredSize(const View* host) const { |
| 150 return gfx::Size(0, GetPreferredHeightForWidth(host, 0)); |
| 151 } |
| 152 |
| 153 int AlignLayoutState::GetPreferredHeightForWidth(const View* host, |
| 154 int width) const { |
| 155 int height = 0; |
| 156 for (int i = 0; i < host->child_count(); ++i) { |
| 157 Align align; |
| 158 const View* child = host->child_at(i); |
| 159 if (child->visible() && FindAlign(child, &align) && |
| 160 (align == views::Align::Top || align == views::Align::Bottom || |
| 161 align == views::Align::Content)) { |
| 162 height += child->GetPreferredSize().height(); |
| 163 } |
| 164 } |
| 165 return height; |
| 166 } |
| 167 |
| 168 static int MulDiv(int Number, int Numerator, int Denominator) { |
| 169 return static_cast<int>((static_cast<int64_t>(Number) * Numerator) / |
| 170 Denominator); |
| 171 } |
| 172 |
| 173 void AlignLayoutState::PlaceView(View* view, |
| 174 views::Align align, |
| 175 gfx::Rect& contents) { |
| 176 AnchorState state; |
| 177 AnchorState* anchor_state = &state; |
| 178 FindAnchorState(view, &anchor_state); |
| 179 if (align == Align_None || |
| 180 anchor_state->anchors != AnchorContent::AnchorAlign(align)) { |
| 181 if (!last_host_size_.IsEmpty()) { |
| 182 int new_left = view->x(); |
| 183 int new_top = view->y(); |
| 184 int new_width = view->width(); |
| 185 int new_height = view->height(); |
| 186 Anchors anchors = anchor_state->anchors; |
| 187 gfx::Size parent_size = view->parent()->GetContentsBounds().size(); |
| 188 if (anchors.Has(Anchor::Right)) |
| 189 if (anchors.Has(Anchor::Left)) |
| 190 new_width = parent_size.width() - |
| 191 (last_host_size_.width() - |
| 192 anchor_state->anchor_basis.x()); |
| 193 else |
| 194 new_left = parent_size.width() - |
| 195 (last_host_size_.width() - anchor_state->anchor_basis.x()); |
| 196 else if (!anchors.Has(Anchor::Left)) |
| 197 new_left = MulDiv(anchor_state->anchor_basis.x(), parent_size.width(), |
| 198 last_host_size_.width()) - |
| 199 new_width / 2; |
| 200 if (anchors.Has(Anchor::Bottom)) |
| 201 if (anchors.Has(Anchor::Top)) |
| 202 new_height = parent_size.height() - (last_host_size_.height() - |
| 203 anchor_state->anchor_basis.y()); |
| 204 else |
| 205 new_top = parent_size.height() - |
| 206 (last_host_size_.height() - anchor_state->anchor_basis.y()); |
| 207 else if (!anchors.Has(Anchor::Top)) |
| 208 new_top = MulDiv(anchor_state->anchor_basis.y(), parent_size.height(), |
| 209 last_host_size_.height()) - |
| 210 new_height / 2; |
| 211 view->SetBounds(new_left, new_top, new_width, new_height); |
| 212 } |
| 213 if (align == Align_None) |
| 214 return; |
| 215 } |
| 216 gfx::Size preferred_size = view->GetPreferredSize(); |
| 217 if (preferred_size.IsEmpty()) { |
| 218 // TODO(robliao): Remove this once views has a way to handle preferred size |
| 219 // correctly. |
| 220 preferred_size = view->size(); |
| 221 } |
| 222 int new_width = contents.size().width(); |
| 223 if (new_width < 0 || align == views::Align::Left || |
| 224 align == views::Align::Right) { |
| 225 new_width = preferred_size.width(); |
| 226 } |
| 227 |
| 228 int new_height = contents.size().height(); |
| 229 if (new_height < 0 || align == views::Align::Top || |
| 230 align == views::Align::Bottom) |
| 231 new_height = preferred_size.height(); |
| 232 int new_left = contents.x(); |
| 233 int new_top = contents.y(); |
| 234 switch (align) { |
| 235 case views::Align::Top: { |
| 236 contents.Inset(0, new_height, 0, 0); |
| 237 break; |
| 238 } |
| 239 case views::Align::Bottom: { |
| 240 contents.Inset(0, 0, 0, new_height); |
| 241 new_top = contents.bottom(); |
| 242 break; |
| 243 } |
| 244 case views::Align::Left: { |
| 245 contents.Inset(new_width, 0, 0, 0); |
| 246 break; |
| 247 } |
| 248 case views::Align::Right: { |
| 249 contents.Inset(0, 0, new_width, 0); |
| 250 new_left = contents.right(); |
| 251 // Fall through |
| 252 } |
| 253 default: |
| 254 break; |
| 255 } |
| 256 view->SetBounds(new_left, new_top, new_width, new_height); |
| 257 // If the view's bounds are constrained in some other manner, this |
| 258 // will ensure the content rect is adjusted based on the actual |
| 259 // size of the view. |
| 260 if (view->width() != new_width || view->height() != new_height) { |
| 261 switch (align) { |
| 262 case views::Align::Top: { |
| 263 contents.set_y(contents.y() - (new_height - view->height())); |
| 264 break; |
| 265 } |
| 266 case views::Align::Bottom: { |
| 267 contents.set_height(contents.height() + (new_height - view->height())); |
| 268 break; |
| 269 } |
| 270 case views::Align::Left: { |
| 271 contents.set_x(contents.x() - (new_width - view->width())); |
| 272 break; |
| 273 } |
| 274 case views::Align::Right: { |
| 275 contents.set_width(contents.width() + (new_width - view->width())); |
| 276 break; |
| 277 } |
| 278 case views::Align::Content: { |
| 279 contents.set_width(contents.width() + (new_width - view->width())); |
| 280 contents.set_height(contents.height() + (new_height - view->width())); |
| 281 } |
| 282 } |
| 283 } |
| 284 } |
| 285 |
| 286 void AlignLayoutState::SetViewBounds(View* view, |
| 287 int x, |
| 288 int y, |
| 289 int width, |
| 290 int height) { |
| 291 DCHECK(view); |
| 292 view->SetBounds(x, y, width, height); |
| 293 UpdateAnchorBasis(view); |
| 294 } |
| 295 |
| 296 bool AlignLayoutState::ShouldAlign(View* host) { |
| 297 for (int i = 0; i < host->child_count(); ++i) { |
| 298 View* view = host->child_at(i); |
| 299 if (view->visible() && |
| 300 (FindAlign(view, nullptr) || FindAnchorState(view, nullptr))) |
| 301 return true; |
| 302 } |
| 303 return false; |
| 304 } |
| 305 |
| 306 bool AlignLayoutState::ShouldInsert(View* child1, |
| 307 View* child2, |
| 308 Align align) { |
| 309 switch (align) { |
| 310 case views::Align::Top: |
| 311 return child1->y() < child2->y(); |
| 312 case views::Align::Bottom: |
| 313 return child1->bounds().bottom() >= child2->bounds().bottom(); |
| 314 case views::Align::Left: |
| 315 return child1->x() < child2->x(); |
| 316 case views::Align::Right: |
| 317 return child1->bounds().right() >= child2->bounds().right(); |
| 318 default: |
| 319 return false; |
| 320 } |
| 321 } |
| 322 |
| 323 void AlignLayoutState::UpdateAnchorAlign(View* view, Align align) { |
| 324 AnchorMap::iterator pos = anchor_map_.find(view); |
| 325 if (pos != anchor_map_.end()) { |
| 326 pos->second.anchors = AnchorContent::AnchorAlign(align); |
| 327 InternalUpdateAnchorBasis(view, pos); |
| 328 } |
| 329 } |
| 330 |
| 331 void AlignLayoutState::UpdateAnchorBasis(View* view) { |
| 332 AnchorMap::iterator pos = anchor_map_.find(view); |
| 333 if (pos != anchor_map_.end() && |
| 334 pos->second.anchors != Anchors({Anchor::Left, Anchor::Top})) |
| 335 InternalUpdateAnchorBasis(view, pos); |
| 336 } |
| 337 |
| 338 void AlignLayoutState::ViewAdded(View* host, View* view) { |
| 339 UpdateAnchorBasis(view); |
| 340 } |
| 341 |
| 342 void AlignLayoutState::ViewRemoved(View* host, View* view) { |
| 343 NoAlignView(view); |
| 344 NoAnchorView(view); |
| 345 } |
| 346 |
| 347 } // namespace views |
OLD | NEW |