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

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

Issue 2836313002: BoxLayout now suports per-view margins. (Closed)
Patch Set: Added margin collapsing and a BoxLayout example Created 3 years, 7 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
Index: ui/views/layout/box_layout.cc
diff --git a/ui/views/layout/box_layout.cc b/ui/views/layout/box_layout.cc
index 7e5abff4880af33a0b34583c3d0c7f127b7480c5..f2897f2225da3908613689d1f654e81f17d628c8 100644
--- a/ui/views/layout/box_layout.cc
+++ b/ui/views/layout/box_layout.cc
@@ -11,10 +11,49 @@
namespace views {
+BoxLayout::ViewWrapper::ViewWrapper() : view_(nullptr), layout_(nullptr) {}
+
+BoxLayout::ViewWrapper::ViewWrapper(const BoxLayout* layout, View* view)
+ : view_(view), layout_(layout) {
+ gfx::Insets* margins = view_ ? view_->GetProperty(kMarginsKey) : nullptr;
+ if (margins)
+ margins_ = *margins;
+}
+
+BoxLayout::ViewWrapper::~ViewWrapper() {}
+
+int BoxLayout::ViewWrapper::GetHeightForWidth(int width) const {
+ return layout_->collapse_margins_spacing_
+ ? view_->GetHeightForWidth(width)
+ : view_->GetHeightForWidth(width - margins_.width()) +
sky 2017/05/09 19:26:03 Make sure you don't go negative here.
kylix_rd 2017/05/10 20:35:19 Done.
+ margins_.height();
+}
+
+gfx::Size BoxLayout::ViewWrapper::GetPreferredSize() const {
+ gfx::Size preferred_size = view_->GetPreferredSize();
+ if (!layout_->collapse_margins_spacing_)
+ preferred_size.Enlarge(margins_.width(), margins_.height());
+ return preferred_size;
+}
+
+void BoxLayout::ViewWrapper::SetBoundsRect(const gfx::Rect& bounds) {
+ if (layout_->collapse_margins_spacing_)
+ view_->SetBounds(bounds.x(), bounds.y(), bounds.width(), bounds.height());
+ else
+ view_->SetBounds(bounds.x() + margins_.left(), bounds.y() + margins_.top(),
sky 2017/05/09 19:26:03 optional: use {} around this if given the body spa
kylix_rd 2017/05/10 20:35:19 Done.
+ bounds.width() - margins_.width(),
sky 2017/05/09 19:26:03 Do you need to protect against going negative?
kylix_rd 2017/05/10 20:35:19 Done.
+ bounds.height() - margins_.height());
+}
+
+bool BoxLayout::ViewWrapper::visible() const {
+ return view_->visible();
+}
+
BoxLayout::BoxLayout(BoxLayout::Orientation orientation,
int inside_border_horizontal_spacing,
int inside_border_vertical_spacing,
- int between_child_spacing)
+ int between_child_spacing,
+ bool collapse_margins_spacing)
: orientation_(orientation),
inside_border_insets_(inside_border_vertical_spacing,
inside_border_horizontal_spacing),
@@ -23,8 +62,18 @@ BoxLayout::BoxLayout(BoxLayout::Orientation orientation,
cross_axis_alignment_(CROSS_AXIS_ALIGNMENT_STRETCH),
default_flex_(0),
minimum_cross_axis_size_(0),
- host_(NULL) {
-}
+ collapse_margins_spacing_(collapse_margins_spacing),
+ host_(NULL) {}
+
+BoxLayout::BoxLayout(BoxLayout::Orientation orientation,
+ int inside_border_horizontal_spacing,
+ int inside_border_vertical_spacing,
+ int between_child_spacing)
+ : BoxLayout(orientation,
+ inside_border_horizontal_spacing,
+ inside_border_vertical_spacing,
+ between_child_spacing,
+ false) {}
BoxLayout::~BoxLayout() {
}
@@ -49,23 +98,25 @@ void BoxLayout::SetDefaultFlex(int default_flex) {
void BoxLayout::Layout(View* host) {
DCHECK_EQ(host_, host);
- gfx::Rect child_area(host->GetLocalBounds());
- child_area.Inset(host->GetInsets());
+ gfx::Rect child_area(host->GetContentsBounds());
child_area.Inset(inside_border_insets_);
+ AdjustMainAxisForMargin(&child_area);
int total_main_axis_size = 0;
int num_visible = 0;
int flex_sum = 0;
// Calculate the total size of children in the main axis.
for (int i = 0; i < host->child_count(); ++i) {
- View* child = host->child_at(i);
- if (!child->visible())
+ ViewWrapper child(this, host->child_at(i));
+ ViewWrapper next(this, NextVisibleView(host, i));
+ if (!child.visible())
continue;
- int flex = GetFlexForView(child);
+ int flex = GetFlexForView(child.view());
int child_main_axis_size = MainAxisSizeForView(child, child_area.width());
if (child_main_axis_size == 0 && flex == 0)
continue;
- total_main_axis_size += child_main_axis_size + between_child_spacing_;
+ total_main_axis_size +=
+ child_main_axis_size + MainAxisMarginBetweenViews(child, next);
++num_visible;
flex_sum += flex;
}
@@ -106,8 +157,9 @@ void BoxLayout::Layout(View* host) {
int total_padding = 0;
int current_flex = 0;
for (int i = 0; i < host->child_count(); ++i) {
- View* child = host->child_at(i);
- if (!child->visible())
+ ViewWrapper child(this, host->child_at(i));
+ ViewWrapper next(this, NextVisibleView(host, i));
+ if (!child.visible())
continue;
// TODO(bruthig): Fix this. The main axis should be calculated before
@@ -131,8 +183,8 @@ void BoxLayout::Layout(View* host) {
// Calculate flex padding.
int current_padding = 0;
- if (GetFlexForView(child) > 0) {
- current_flex += GetFlexForView(child);
+ if (GetFlexForView(child.view()) > 0) {
+ current_flex += GetFlexForView(child.view());
int quot = (main_free_space * current_flex) / flex_sum;
int rem = (main_free_space * current_flex) % flex_sum;
current_padding = quot - total_padding;
@@ -147,12 +199,14 @@ void BoxLayout::Layout(View* host) {
// See https://crbug.com/682266.
int child_main_axis_size = MainAxisSizeForView(child, child_area.width());
SetMainAxisSize(child_main_axis_size + current_padding, &bounds);
- if (MainAxisSize(bounds) > 0 || GetFlexForView(child) > 0)
- main_position += MainAxisSize(bounds) + between_child_spacing_;
+ if (MainAxisSize(bounds) > 0 || GetFlexForView(child.view()) > 0)
+ main_position +=
+ MainAxisSize(bounds) + MainAxisMarginBetweenViews(child, next);
// Clamp child view bounds to |child_area|.
bounds.Intersect(child_area);
- child->SetBoundsRect(bounds);
+ AdjustCrossAxisForMargin(child, child_area, &bounds);
+ child.SetBoundsRect(bounds);
}
// Flex views should have grown/shrunk to consume all free space.
@@ -165,12 +219,12 @@ gfx::Size BoxLayout::GetPreferredSize(const View* host) const {
// Calculate the child views' preferred width.
int width = 0;
if (orientation_ == kVertical) {
- for (int i = 0; i < host->child_count(); ++i) {
- const View* child = host->child_at(i);
- if (!child->visible())
+ for (int i = 0; i < host_->child_count(); ++i) {
+ const ViewWrapper child(this, host_->child_at(i));
+ if (!child.visible())
continue;
- width = std::max(width, child->GetPreferredSize().width());
+ width = std::max(width, child.GetPreferredSize().width());
}
width = std::max(width, minimum_cross_axis_size_);
}
@@ -194,7 +248,7 @@ void BoxLayout::ViewRemoved(View* host, View* view) {
}
int BoxLayout::GetFlexForView(const View* view) const {
- std::map<const View*, int>::const_iterator it = flex_map_.find(view);
+ FlexMap::const_iterator it = flex_map_.find(view);
if (it == flex_map_.end())
return default_flex_;
@@ -245,26 +299,97 @@ void BoxLayout::SetCrossAxisPosition(int position, gfx::Rect* rect) const {
rect->set_y(position);
}
-int BoxLayout::MainAxisSizeForView(const View* view,
+int BoxLayout::MainAxisSizeForView(const ViewWrapper& view,
int child_area_width) const {
return orientation_ == kHorizontal
- ? view->GetPreferredSize().width()
- : view->GetHeightForWidth(cross_axis_alignment_ ==
- CROSS_AXIS_ALIGNMENT_STRETCH
- ? child_area_width
- : view->GetPreferredSize().width());
+ ? view.GetPreferredSize().width()
+ : view.GetHeightForWidth(cross_axis_alignment_ ==
+ CROSS_AXIS_ALIGNMENT_STRETCH
+ ? child_area_width
+ : view.GetPreferredSize().width());
}
-int BoxLayout::CrossAxisSizeForView(const View* view) const {
+int BoxLayout::MainAxisMarginBetweenViews(const ViewWrapper& left,
+ const ViewWrapper& right) const {
+ return orientation_ == Orientation::kHorizontal
+ ? (collapse_margins_spacing_ && left.view() && right.view()
+ ? std::max(between_child_spacing_,
+ std::max(left.margins().right(),
+ right.margins().left()))
+ : between_child_spacing_)
+ : (collapse_margins_spacing_ && left.view() && right.view()
+ ? std::max(between_child_spacing_,
+ std::max(left.margins().bottom(),
+ right.margins().top()))
+ : between_child_spacing_);
+}
+
+void BoxLayout::AdjustMainAxisForMargin(gfx::Rect* rect) const {
+ if (collapse_margins_spacing_) {
sky 2017/05/09 19:26:03 Generally we prefer early return than lots of nest
kylix_rd 2017/05/09 20:07:06 "if" block gone in subsequent patch.
+ const ViewWrapper first(this, FirstVisibleView(host_));
+ const ViewWrapper last(this, LastVisibleView(host_));
+ if (!(first.view() && last.view()))
+ return;
+ if (orientation_ == Orientation::kHorizontal) {
+ rect->set_x(rect->x() + std::max(inside_border_insets_.left(),
+ first.margins().left()));
+ rect->set_width((rect->right() - std::max(inside_border_insets_.right(),
sky 2017/05/09 19:26:03 Make sure you don't end up with a negative width (
kylix_rd 2017/05/10 20:35:19 Done.
+ last.margins().right())) -
+ rect->x());
+ } else {
+ rect->set_y(rect->y() +
+ std::max(inside_border_insets_.top(), first.margins().top()));
+ rect->set_height(
+ (rect->bottom() -
+ std::max(inside_border_insets_.bottom(), last.margins().bottom())) -
+ rect->y());
+ }
+ }
+}
+
+void BoxLayout::AdjustCrossAxisForMargin(const ViewWrapper& child,
sky 2017/05/09 19:26:03 I don't think this implements what I outlined in c
kylix_rd 2017/05/09 20:07:06 In all modes other than stretch, they'll be stagge
sky 2017/05/09 23:55:29 That makes it rather painful to set margins, in so
+ const gfx::Rect& client,
+ gfx::Rect* rect) const {
+ if (collapse_margins_spacing_) {
+ if (orientation_ == Orientation::kVertical) {
+ rect->SetByBounds(
+ rect->x() > client.x()
+ ? rect->x()
+ : rect->x() + std::max(inside_border_insets_.left(),
+ child.margins().left()),
+ rect->y(),
+ rect->right() < client.right()
+ ? rect->right()
+ : rect->right() - std::max(inside_border_insets_.right(),
+ child.margins().right()),
+ rect->bottom());
+ } else {
+ rect->SetByBounds(
+ rect->x(),
+ rect->y() > client.y()
+ ? rect->y()
+ : rect->y() + std::max(inside_border_insets_.top(),
+ child.margins().top()),
+ rect->right(),
+ rect->bottom() < client.bottom()
+ ? rect->bottom()
+ : rect->bottom() - std::max(inside_border_insets_.bottom(),
+ child.margins().bottom()));
+ }
+ }
+}
+
+int BoxLayout::CrossAxisSizeForView(const ViewWrapper& view) const {
// TODO(bruthig): For horizontal case use the available width and not the
// preferred width. See https://crbug.com/682266.
return orientation_ == kVertical
- ? view->GetPreferredSize().width()
- : view->GetHeightForWidth(view->GetPreferredSize().width());
+ ? view.GetPreferredSize().width()
+ : view.GetHeightForWidth(view.GetPreferredSize().width());
}
gfx::Size BoxLayout::GetPreferredSizeForChildWidth(const View* host,
int child_area_width) const {
+ DCHECK_EQ(host, host_);
gfx::Rect child_area_bounds;
if (orientation_ == kHorizontal) {
@@ -272,34 +397,36 @@ gfx::Size BoxLayout::GetPreferredSizeForChildWidth(const View* host,
// default behavior of GridLayout::GetPreferredHeightForWidth().
// TODO(estade|bruthig): Fix this See // https://crbug.com/682266.
int position = 0;
- for (int i = 0; i < host->child_count(); ++i) {
- const View* child = host->child_at(i);
- if (!child->visible())
+ for (int i = 0; i < host_->child_count(); ++i) {
+ const ViewWrapper child(this, host_->child_at(i));
+ const ViewWrapper next(this, NextVisibleView(host, i));
+ if (!child.visible())
continue;
- gfx::Size size(child->GetPreferredSize());
+ gfx::Size size(child.GetPreferredSize());
if (size.IsEmpty())
continue;
gfx::Rect child_bounds(position, 0, size.width(), size.height());
child_area_bounds.Union(child_bounds);
- position += size.width() + between_child_spacing_;
+ position += size.width() + MainAxisMarginBetweenViews(child, next);
}
child_area_bounds.set_height(
std::max(child_area_bounds.height(), minimum_cross_axis_size_));
} else {
int height = 0;
- for (int i = 0; i < host->child_count(); ++i) {
- const View* child = host->child_at(i);
- if (!child->visible())
+ for (int i = 0; i < host_->child_count(); ++i) {
+ const ViewWrapper child(this, host_->child_at(i));
+ const ViewWrapper next(this, NextVisibleView(host, i));
+ if (!child.visible())
continue;
// Use the child area width for getting the height if the child is
// supposed to stretch. Use its preferred size otherwise.
int extra_height = MainAxisSizeForView(child, child_area_width);
// Only add |between_child_spacing_| if this is not the only child.
- if (height != 0 && extra_height > 0)
- height += between_child_spacing_;
+ if (next.view() && extra_height > 0)
+ height += MainAxisMarginBetweenViews(child, next);
height += extra_height;
}
@@ -307,7 +434,7 @@ gfx::Size BoxLayout::GetPreferredSizeForChildWidth(const View* host,
child_area_bounds.set_height(height);
}
- gfx::Size non_child_size = NonChildSize(host);
+ gfx::Size non_child_size = NonChildSize(host_);
return gfx::Size(child_area_bounds.width() + non_child_size.width(),
child_area_bounds.height() + non_child_size.height());
}
@@ -318,4 +445,28 @@ gfx::Size BoxLayout::NonChildSize(const View* host) const {
insets.height() + inside_border_insets_.height());
}
+View* BoxLayout::NextVisibleView(View* host, int index) const {
+ DCHECK_EQ(host_, host);
sky 2017/05/09 19:26:03 Why bother with passing in host when host_ is alre
kylix_rd 2017/05/09 20:07:06 I was keeping the established pattern... odd that
+ for (int i = index + 1; i < host->child_count(); ++i) {
+ View* result = host->child_at(i);
+ if (result->visible())
+ return result;
+ }
+ return nullptr;
+}
+
+View* BoxLayout::FirstVisibleView(View* host) const {
+ return NextVisibleView(host, -1);
+}
+
+View* BoxLayout::LastVisibleView(View* host) const {
+ DCHECK_EQ(host_, host);
+ for (int i = host->child_count() - 1; i >= 0; --i) {
+ View* result = host->child_at(i);
+ if (result->visible())
+ return result;
+ }
+ return nullptr;
+}
+
} // namespace views

Powered by Google App Engine
This is Rietveld 408576698