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

Unified Diff: ui/views/layout/align_layout_state.cc

Issue 2445633003: Experimental alignment layout manager using state retained in the layout manager
Patch Set: Created 4 years, 2 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « ui/views/layout/align_layout_state.h ('k') | ui/views/layout/align_layout_state_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ui/views/layout/align_layout_state.cc
diff --git a/ui/views/layout/align_layout_state.cc b/ui/views/layout/align_layout_state.cc
new file mode 100644
index 0000000000000000000000000000000000000000..8c8eaf2830a862046dc10dc5ce3ecbd122a4ea08
--- /dev/null
+++ b/ui/views/layout/align_layout_state.cc
@@ -0,0 +1,347 @@
+// 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_state.h"
+
+#include "base/logging.h"
+
+namespace views {
+
+// This special value represents the absence of the align attribute.
+static constexpr Align Align_None =
+ static_cast<Align>(static_cast<int>(Align::Content) + 1);
+
+AlignLayoutState::AnchorState::AnchorState()
+ : anchors({Anchor::Top, Anchor::Left}), anchor_basis(0, 0) {}
+
+AlignLayoutState::AnchorState::AnchorState(Anchors anchors)
+ : anchors(anchors), anchor_basis(0, 0) {}
+
+AlignLayoutState::AnchorState::AnchorState(const AnchorState& state)
+ : anchors(state.anchors), anchor_basis(state.anchor_basis) {}
+
+AlignLayoutState::AnchorState::~AnchorState() {}
+
+AlignLayoutState::AlignLayoutState() {}
+
+AlignLayoutState::~AlignLayoutState() {}
+
+void AlignLayoutState::AlignView(View* view, Align align) {
+ AlignMap::iterator pos = align_map_.find(view);
+ if (pos == align_map_.end())
+ align_map_.emplace(std::make_pair(view, align));
+ else
+ pos->second = align;
+ UpdateAnchorAlign(view, align);
+}
+
+void AlignLayoutState::AnchorView(View* view, Anchors anchors) {
+ AnchorMap::iterator pos = anchor_map_.find(view);
+ if (pos == anchor_map_.end())
+ pos = anchor_map_.emplace(std::make_pair(view, AnchorState(anchors))).first;
+ else
+ pos->second.anchors = anchors;
+ UpdateAnchorBasis(view);
+}
+
+void AlignLayoutState::AnchorView(
+ View* view,
+ const std::initializer_list<Anchor>& anchors) {
+ AnchorView(view, Anchors(anchors));
+}
+
+void AlignLayoutState::AlignViews(View* host,
+ views::Align align,
+ gfx::Rect& contents) {
+ View::Views align_list;
+ for (int i = 0; i < host->child_count(); ++i) {
+ Align child_align;
+ View* child = host->child_at(i);
+ if (child->visible() && FindAlign(child, &child_align)
+ ? child_align == align
+ : align == Align_None) {
+ View::Views::iterator j = align_list.begin();
+ while (j < align_list.end() && !ShouldInsert(child, *j, align))
+ j++;
+ align_list.insert(j, child);
+ }
+ }
+ for (View* child : align_list) {
+ PlaceView(child, align, contents);
+ UpdateAnchorBasis(child);
+ }
+}
+
+bool AlignLayoutState::FindAlign(View* view, Align* align) {
+ AlignMap::iterator pos = align_map_.find(view);
+ if (pos != align_map_.end()) {
+ if (align)
+ *align = pos->second;
+ return true;
+ }
+ return false;
+}
+
+bool AlignLayoutState::FindAlign(const View* view, Align* align) const {
+ return const_cast<AlignLayoutState*>(this)->FindAlign(const_cast<View*>(view),
+ align);
+}
+
+bool AlignLayoutState::FindAnchorState(View* view, AnchorState** state) {
+ AnchorMap::iterator pos = anchor_map_.find(view);
+ if (pos != anchor_map_.end()) {
+ if (state)
+ *state = &pos->second;
+ return true;
+ }
+ return false;
+}
+
+void AlignLayoutState::InternalUpdateAnchorBasis(View* view,
+ AnchorMap::iterator pos) {
+ AnchorState& state = pos->second;
+ gfx::Point center_point = view->bounds().CenterPoint();
+ if (state.anchors.Has(Anchor::Right))
+ if (state.anchors.Has(Anchor::Left))
+ state.anchor_basis.set_x(view->width());
+ else
+ state.anchor_basis.set_x(view->x());
+ else
+ state.anchor_basis.set_x(center_point.x());
+ if (state.anchors.Has(Anchor::Bottom))
+ if (state.anchors.Has(Anchor::Top))
+ state.anchor_basis.set_y(view->height());
+ else
+ state.anchor_basis.set_y(view->y());
+ else
+ state.anchor_basis.set_y(center_point.y());
+}
+
+void AlignLayoutState::Installed(View* host) {
+ last_host_size_ = host->size();
+}
+
+void AlignLayoutState::Layout(View* host) {
+ static const Align alignstyle[] = {
+ views::Align::Top, views::Align::Bottom, views::Align::Left,
+ views::Align::Right, views::Align::Content, Align_None};
+ if (ShouldAlign(host)) {
+ gfx::Rect contents = host->GetContentsBounds();
+ for (std::size_t i = 0; i < sizeof(alignstyle) / sizeof(alignstyle[0]); i++)
+ AlignViews(host, alignstyle[i], contents);
+ last_host_size_ = host->size();
+ }
+}
+
+void AlignLayoutState::NoAlignView(View* view) {
+ AlignMap::iterator pos = align_map_.find(view);
+ if (pos != align_map_.end())
+ align_map_.erase(pos);
+}
+
+void AlignLayoutState::NoAnchorView(View* view) {
+ AnchorMap::iterator pos = anchor_map_.find(view);
+ if (pos != anchor_map_.end())
+ anchor_map_.erase(pos);
+}
+
+gfx::Size AlignLayoutState::GetPreferredSize(const View* host) const {
+ return gfx::Size(0, GetPreferredHeightForWidth(host, 0));
+}
+
+int AlignLayoutState::GetPreferredHeightForWidth(const View* host,
+ int width) const {
+ int height = 0;
+ for (int i = 0; i < host->child_count(); ++i) {
+ Align align;
+ const View* child = host->child_at(i);
+ if (child->visible() && FindAlign(child, &align) &&
+ (align == views::Align::Top || align == views::Align::Bottom ||
+ align == views::Align::Content)) {
+ height += child->GetPreferredSize().height();
+ }
+ }
+ return height;
+}
+
+static int MulDiv(int Number, int Numerator, int Denominator) {
+ return static_cast<int>((static_cast<int64_t>(Number) * Numerator) /
+ Denominator);
+}
+
+void AlignLayoutState::PlaceView(View* view,
+ views::Align align,
+ gfx::Rect& contents) {
+ AnchorState state;
+ AnchorState* anchor_state = &state;
+ FindAnchorState(view, &anchor_state);
+ if (align == Align_None ||
+ anchor_state->anchors != AnchorContent::AnchorAlign(align)) {
+ if (!last_host_size_.IsEmpty()) {
+ int new_left = view->x();
+ int new_top = view->y();
+ int new_width = view->width();
+ int new_height = view->height();
+ Anchors anchors = anchor_state->anchors;
+ gfx::Size parent_size = view->parent()->GetContentsBounds().size();
+ if (anchors.Has(Anchor::Right))
+ if (anchors.Has(Anchor::Left))
+ new_width = parent_size.width() -
+ (last_host_size_.width() -
+ anchor_state->anchor_basis.x());
+ else
+ new_left = parent_size.width() -
+ (last_host_size_.width() - anchor_state->anchor_basis.x());
+ else if (!anchors.Has(Anchor::Left))
+ new_left = MulDiv(anchor_state->anchor_basis.x(), parent_size.width(),
+ last_host_size_.width()) -
+ new_width / 2;
+ if (anchors.Has(Anchor::Bottom))
+ if (anchors.Has(Anchor::Top))
+ new_height = parent_size.height() - (last_host_size_.height() -
+ anchor_state->anchor_basis.y());
+ else
+ new_top = parent_size.height() -
+ (last_host_size_.height() - anchor_state->anchor_basis.y());
+ else if (!anchors.Has(Anchor::Top))
+ new_top = MulDiv(anchor_state->anchor_basis.y(), parent_size.height(),
+ last_host_size_.height()) -
+ new_height / 2;
+ view->SetBounds(new_left, new_top, new_width, new_height);
+ }
+ if (align == Align_None)
+ return;
+ }
+ gfx::Size preferred_size = view->GetPreferredSize();
+ if (preferred_size.IsEmpty()) {
+ // TODO(robliao): Remove this once views has a way to handle preferred size
+ // correctly.
+ preferred_size = view->size();
+ }
+ int new_width = contents.size().width();
+ if (new_width < 0 || align == views::Align::Left ||
+ align == views::Align::Right) {
+ new_width = preferred_size.width();
+ }
+
+ int new_height = contents.size().height();
+ if (new_height < 0 || align == views::Align::Top ||
+ align == views::Align::Bottom)
+ new_height = preferred_size.height();
+ int new_left = contents.x();
+ int new_top = contents.y();
+ switch (align) {
+ case views::Align::Top: {
+ contents.Inset(0, new_height, 0, 0);
+ break;
+ }
+ case views::Align::Bottom: {
+ contents.Inset(0, 0, 0, new_height);
+ new_top = contents.bottom();
+ break;
+ }
+ case views::Align::Left: {
+ contents.Inset(new_width, 0, 0, 0);
+ break;
+ }
+ case views::Align::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 (align) {
+ case views::Align::Top: {
+ contents.set_y(contents.y() - (new_height - view->height()));
+ break;
+ }
+ case views::Align::Bottom: {
+ contents.set_height(contents.height() + (new_height - view->height()));
+ break;
+ }
+ case views::Align::Left: {
+ contents.set_x(contents.x() - (new_width - view->width()));
+ break;
+ }
+ case views::Align::Right: {
+ contents.set_width(contents.width() + (new_width - view->width()));
+ break;
+ }
+ case views::Align::Content: {
+ contents.set_width(contents.width() + (new_width - view->width()));
+ contents.set_height(contents.height() + (new_height - view->width()));
+ }
+ }
+ }
+}
+
+void AlignLayoutState::SetViewBounds(View* view,
+ int x,
+ int y,
+ int width,
+ int height) {
+ DCHECK(view);
+ view->SetBounds(x, y, width, height);
+ UpdateAnchorBasis(view);
+}
+
+bool AlignLayoutState::ShouldAlign(View* host) {
+ for (int i = 0; i < host->child_count(); ++i) {
+ View* view = host->child_at(i);
+ if (view->visible() &&
+ (FindAlign(view, nullptr) || FindAnchorState(view, nullptr)))
+ return true;
+ }
+ return false;
+}
+
+bool AlignLayoutState::ShouldInsert(View* child1,
+ View* child2,
+ Align align) {
+ switch (align) {
+ case views::Align::Top:
+ return child1->y() < child2->y();
+ case views::Align::Bottom:
+ return child1->bounds().bottom() >= child2->bounds().bottom();
+ case views::Align::Left:
+ return child1->x() < child2->x();
+ case views::Align::Right:
+ return child1->bounds().right() >= child2->bounds().right();
+ default:
+ return false;
+ }
+}
+
+void AlignLayoutState::UpdateAnchorAlign(View* view, Align align) {
+ AnchorMap::iterator pos = anchor_map_.find(view);
+ if (pos != anchor_map_.end()) {
+ pos->second.anchors = AnchorContent::AnchorAlign(align);
+ InternalUpdateAnchorBasis(view, pos);
+ }
+}
+
+void AlignLayoutState::UpdateAnchorBasis(View* view) {
+ AnchorMap::iterator pos = anchor_map_.find(view);
+ if (pos != anchor_map_.end() &&
+ pos->second.anchors != Anchors({Anchor::Left, Anchor::Top}))
+ InternalUpdateAnchorBasis(view, pos);
+}
+
+void AlignLayoutState::ViewAdded(View* host, View* view) {
+ UpdateAnchorBasis(view);
+}
+
+void AlignLayoutState::ViewRemoved(View* host, View* view) {
+ NoAlignView(view);
+ NoAnchorView(view);
+}
+
+} // namespace views
« no previous file with comments | « ui/views/layout/align_layout_state.h ('k') | ui/views/layout/align_layout_state_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698