Index: ui/views/layout/align_layout.cc |
diff --git a/ui/views/layout/align_layout.cc b/ui/views/layout/align_layout.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..917207f2d4f69f5f30fe64ca5358e2f322519f4e |
--- /dev/null |
+++ b/ui/views/layout/align_layout.cc |
@@ -0,0 +1,197 @@ |
+// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "ui/views/layout/align_layout.h" |
+ |
+#include "base/logging.h" |
+#include "ui/views/anchor_attribute.h" |
+ |
+namespace views { |
+ |
+// This special value represents the absence of the align attribute. |
+static constexpr Fill Fill_None = |
+ static_cast<Fill>(static_cast<int>(Fill::Content) + 1); |
+ |
+AlignLayout::AlignLayout() {} |
+ |
+AlignLayout::~AlignLayout() {} |
+ |
+void AlignLayout::AlignViews(View* host, |
+ views::Fill fill, |
+ gfx::Rect& contents) { |
+ View::Views fill_list; |
+ for (int i = 0; i < host->child_count(); ++i) { |
+ FillAttribute* fill_attr; |
+ View* child = host->child_at(i); |
+ if (child->visible() && child->attributes().Find(fill_attr) |
+ ? fill_attr->fill() == fill |
+ : fill == Fill_None) { |
+ View::Views::iterator j = fill_list.begin(); |
+ while (j < fill_list.end() && !ShouldInsert(child, *j, fill)) |
+ j++; |
+ fill_list.insert(j, child); |
+ } |
+ } |
+ for (View* child : fill_list) |
+ PlaceView(child, fill, contents); |
+} |
+ |
+void AlignLayout::Layout(View* host) { |
+ static const Fill fillstyles[] = { |
+ views::Fill::Top, views::Fill::Bottom, views::Fill::Left, |
+ views::Fill::Right, views::Fill::Content, Fill_None}; |
+ if (ShouldAlign(host)) { |
+ gfx::Rect contents = host->GetContentsBounds(); |
+ for (std::size_t i = 0; i < sizeof(fillstyles) / sizeof(fillstyles[0]); i++) |
+ AlignViews(host, fillstyles[i], contents); |
+ } |
+} |
+ |
+gfx::Size AlignLayout::GetPreferredSize(const View* host) const { |
+ return gfx::Size(); |
+} |
+ |
+int AlignLayout::GetPreferredHeightForWidth(const View* host, int width) const { |
+ return 0; |
+} |
+ |
+static int MulDiv(int Number, int Numerator, int Denominator) { |
+ return static_cast<int>((static_cast<int64_t>(Number) * Numerator) / |
+ Denominator); |
+} |
+ |
+void AlignLayout::PlaceView(View* view, |
+ views::Fill fill, |
+ gfx::Rect& contents) { |
+ AnchorAttribute* anchor = nullptr; |
+ AnchorContent local_content; |
+ AnchorContent& content = |
+ view->attributes().Find(anchor) ? anchor->GetContent() : local_content; |
+ if (fill == Fill_None || |
+ content.anchors() != AnchorContent::AnchorFill(fill)) { |
+ if (!content.LastParentSize().IsEmpty()) { |
+ int new_left = view->x(); |
+ int new_top = view->y(); |
+ int new_width = view->width(); |
+ int new_height = view->height(); |
+ Anchors anchors = content.anchors(); |
+ gfx::Size parent_size = view->parent()->GetContentsBounds().size(); |
+ if (anchors.Contains(Anchor::Right)) |
+ if (anchors.Contains(Anchor::Left)) |
+ new_width = parent_size.width() - (content.LastParentSize().width() - |
+ content.anchorBasis().x()); |
+ else |
+ new_left = parent_size.width() - (content.LastParentSize().width() - |
+ content.anchorBasis().x()); |
+ else if (!anchors.Contains(Anchor::Left)) |
+ new_left = MulDiv(content.anchorBasis().x(), parent_size.width(), |
+ content.LastParentSize().width()) - |
+ new_width / 2; |
+ if (anchors.Contains(Anchor::Bottom)) |
+ if (anchors.Contains(Anchor::Top)) |
+ new_height = |
+ parent_size.height() - |
+ (content.LastParentSize().height() - content.anchorBasis().y()); |
+ else |
+ new_top = parent_size.height() - (content.LastParentSize().height() - |
+ content.anchorBasis().y()); |
+ else if (!anchors.Contains(Anchor::Top)) |
+ new_top = MulDiv(content.anchorBasis().y(), parent_size.height(), |
+ content.LastParentSize().height()) - |
+ new_height / 2; |
+ view->SetBounds(new_left, new_top, new_width, new_height); |
+ } |
+ if (fill == Fill_None) |
+ return; |
+ } |
+ int new_width = contents.size().width(); |
+ if (new_width < 0 || fill == views::Fill::Left || |
+ fill == views::Fill::Right) |
+ new_width = view->width(); |
+ int new_height = contents.size().height(); |
+ if (new_height < 0 || fill == views::Fill::Top || |
+ fill == views::Fill::Bottom) |
+ new_height = view->height(); |
+ int new_left = contents.x(); |
+ int new_top = contents.y(); |
+ switch (fill) { |
+ case views::Fill::Top: { |
+ contents.Inset(0, new_height, 0, 0); |
+ break; |
+ } |
+ case views::Fill::Bottom: { |
+ contents.Inset(0, 0, 0, new_height); |
+ new_top = contents.bottom(); |
+ break; |
+ } |
+ case views::Fill::Left: { |
+ contents.Inset(new_width, 0, 0, 0); |
+ break; |
+ } |
+ case views::Fill::Right: { |
+ contents.Inset(0, 0, new_width, 0); |
+ new_left = contents.right(); |
+ // Fall through |
+ } |
+ default: |
+ break; |
+ } |
+ view->SetBounds(new_left, new_top, new_width, new_height); |
+ // If the view's bounds are constrained in some other manner, this |
+ // will ensure the content rect is adjusted based on the actual |
+ // size of the view. |
+ if (view->width() != new_width || view->height() != new_height) { |
+ switch (fill) { |
+ case views::Fill::Top: { |
+ contents.set_y(contents.y() - (new_height - view->height())); |
+ break; |
+ } |
+ case views::Fill::Bottom: { |
+ contents.set_height(contents.height() + (new_height - view->height())); |
+ break; |
+ } |
+ case views::Fill::Left: { |
+ contents.set_x(contents.x() - (new_width - view->width())); |
+ break; |
+ } |
+ case views::Fill::Right: { |
+ contents.set_width(contents.width() + (new_width - view->width())); |
+ break; |
+ } |
+ case views::Fill::Content: { |
+ contents.set_width(contents.width() + (new_width - view->width())); |
+ contents.set_height(contents.height() + (new_height - view->width())); |
+ } |
+ } |
+ } |
+} |
+ |
+bool AlignLayout::ShouldAlign(View* host) { |
+ for (int i = 0; i < host->child_count(); ++i) { |
+ FillAttribute* fill_attr; |
+ AnchorAttribute* anchor_attr; |
+ View* view = host->child_at(i); |
+ if (view->visible() && (view->attributes().Find(fill_attr) || |
+ view->attributes().Find(anchor_attr))) |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+bool AlignLayout::ShouldInsert(View* child1, View* child2, views::Fill fill) { |
+ switch (fill) { |
+ case views::Fill::Top: |
+ return child1->y() < child2->y(); |
+ case views::Fill::Bottom: |
+ return child1->bounds().bottom() >= child2->bounds().bottom(); |
+ case views::Fill::Left: |
+ return child1->x() < child2->x(); |
+ case views::Fill::Right: |
+ return child1->bounds().right() >= child2->bounds().right(); |
+ default: |
+ return false; |
+ } |
+} |
+ |
+} // namespace views |