Commit 123efb3e authored by Dana Fried's avatar Dana Fried Committed by Commit Bot

Better support for height-for-width and custom flex rules.

It improves but does not completely fix support for height-for-width
calculations, allowing views to exceed preferred size in more cases
where a custom flex rule or height-for-width relationship is present,
especially in places where there is a vertical layout with horizontal
stretch alignment.

We have also added a specific regression test case for the bug in
question, which has example code showing how to make the requested case
work with the existing infrastructure.

Bug: 1012119
Change-Id: I88380771aacfea29e750dd5033f1fa11e9eb999a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1940714Reviewed-by: default avatarCollin Baker <collinbaker@chromium.org>
Commit-Queue: Dana Fried <dfried@chromium.org>
Cr-Commit-Position: refs/heads/master@{#721236}
parent c810be73
...@@ -402,20 +402,35 @@ void FlexLayout::InitializeChildData( ...@@ -402,20 +402,35 @@ void FlexLayout::InitializeChildData(
flex_child.internal_padding = Normalize( flex_child.internal_padding = Normalize(
orientation(), orientation(),
GetViewProperty(child, layout_defaults_, views::kInternalPaddingKey)); GetViewProperty(child, layout_defaults_, views::kInternalPaddingKey));
flex_child.preferred_size =
Normalize(orientation(), child->GetPreferredSize()); // FlexSpecification defines "preferred size" as the size returned when we
// do not bound the inputs (specifically the main axis). This is different
// from View::GetPreferredSize() which may not take into account e.g. an
// the necessity to alter a view's height in a vertical layout if the width
// is bounded. In the common case this will be equivalent to calling
// GetPreferredSize().
const base::Optional<int> available_cross_size =
GetAvailableCrossAxisSize(*data, view_index, bounds);
// In vertical layouts it's important to consider height-for-width type
// calculations when evaluating the base/preferred size of the child view.
const base::Optional<int> preferred_cross_size =
orientation() == LayoutOrientation::kVertical ? available_cross_size
: base::nullopt;
flex_child.preferred_size = Normalize(
orientation(),
flex_child.flex.rule().Run(
child,
Denormalize(orientation(), {base::nullopt, preferred_cross_size})));
// gfx::Size calculation depends on whether flex is allowed. // gfx::Size calculation depends on whether flex is allowed.
if (main_axis_bounded) { if (main_axis_bounded) {
flex_child.available_size = { flex_child.available_size = {0, available_cross_size};
0, GetAvailableCrossAxisSize(*data, view_index, bounds)};
flex_child.current_size = Normalize( flex_child.current_size = Normalize(
orientation(), orientation(),
flex_child.flex.rule().Run( flex_child.flex.rule().Run(
child, Denormalize(orientation(), flex_child.available_size))); child, Denormalize(orientation(), flex_child.available_size)));
// We should revisit whether this is a valid assumption for text views
// in vertical layouts.
DCHECK_GE(flex_child.preferred_size.main(), DCHECK_GE(flex_child.preferred_size.main(),
flex_child.current_size.main()) flex_child.current_size.main())
<< " in " << child->GetClassName(); << " in " << child->GetClassName();
......
...@@ -61,12 +61,33 @@ int InterpolateSize(MinimumFlexSizeRule minimum_size_rule, ...@@ -61,12 +61,33 @@ int InterpolateSize(MinimumFlexSizeRule minimum_size_rule,
} }
} }
// A view's minimum size can in some cases be expensive to compute. This
// provides a lazy-eval value that behaves like a smart pointer but is more
// lightweight than base::LazyInstance.
class LazyMinimumSize {
public:
explicit LazyMinimumSize(const View* view) : view_(view) {}
~LazyMinimumSize() = default;
const gfx::Size* operator->() const { return get(); }
const gfx::Size& operator*() const { return *get(); }
const gfx::Size* get() const {
if (!size_)
size_ = view_->GetMinimumSize();
return &size_.value();
}
private:
const View* const view_;
mutable base::Optional<gfx::Size> size_;
};
gfx::Size GetPreferredSize(MinimumFlexSizeRule minimum_size_rule, gfx::Size GetPreferredSize(MinimumFlexSizeRule minimum_size_rule,
MaximumFlexSizeRule maximum_size_rule, MaximumFlexSizeRule maximum_size_rule,
bool adjust_height_for_width, bool adjust_height_for_width,
const views::View* view, const View* view,
const views::SizeBounds& maximum_size) { const SizeBounds& maximum_size) {
gfx::Size min = view->GetMinimumSize(); LazyMinimumSize min(view);
gfx::Size preferred = view->GetPreferredSize(); gfx::Size preferred = view->GetPreferredSize();
int width, height; int width, height;
...@@ -76,24 +97,34 @@ gfx::Size GetPreferredSize(MinimumFlexSizeRule minimum_size_rule, ...@@ -76,24 +97,34 @@ gfx::Size GetPreferredSize(MinimumFlexSizeRule minimum_size_rule,
// size; a view can't grow infinitely, so we go with its preferred size. // size; a view can't grow infinitely, so we go with its preferred size.
width = preferred.width(); width = preferred.width();
} else { } else {
width = InterpolateSize(minimum_size_rule, maximum_size_rule, min.width(), width = InterpolateSize(minimum_size_rule, maximum_size_rule, min->width(),
preferred.width(), *maximum_size.width()); preferred.width(), *maximum_size.width());
} }
int preferred_height = preferred.height(); int preferred_height = preferred.height();
if (adjust_height_for_width && width < preferred.width()) { if (adjust_height_for_width) {
// Allow views that need to grow vertically when they're compressed // The |adjust_height_for_width| flag is used in vertical layouts where we
// horizontally to do so. // want views to be able to adapt to the horizontal available space by
// // growing vertically. We therefore allow the horizontal size to shrink even
// If we just went with GetHeightForWidth() we would have situations where // if there's otherwise no flex allowed.
// an empty text control wanted no (or very little) height which could cause if (maximum_size.width() && maximum_size.width() > 0)
// a layout to shrink vertically; we will always try to allocate at least width = std::min(width, *maximum_size.width());
// the view's reported preferred height.
// if (width < preferred.width()) {
// Note that this is an adjustment made for practical considerations, and // Allow views that need to grow vertically when they're compressed
// may not be "correct" in some absolute sense. Let's revisit at some point. // horizontally to do so.
preferred_height = //
std::max(preferred_height, view->GetHeightForWidth(width)); // If we just went with GetHeightForWidth() we would have situations where
// an empty text control wanted no (or very little) height which could
// cause a layout to shrink vertically; we will always try to allocate at
// least the view's reported preferred height.
//
// Note that this is an adjustment made for practical considerations, and
// may not be "correct" in some absolute sense. Let's revisit at some
// point.
preferred_height =
std::max(preferred_height, view->GetHeightForWidth(width));
}
} }
if (!maximum_size.height()) { if (!maximum_size.height()) {
...@@ -101,8 +132,9 @@ gfx::Size GetPreferredSize(MinimumFlexSizeRule minimum_size_rule, ...@@ -101,8 +132,9 @@ gfx::Size GetPreferredSize(MinimumFlexSizeRule minimum_size_rule,
// size; a view can't grow infinitely, so we go with its preferred size. // size; a view can't grow infinitely, so we go with its preferred size.
height = preferred_height; height = preferred_height;
} else { } else {
height = InterpolateSize(minimum_size_rule, maximum_size_rule, min.height(), height =
preferred_height, *maximum_size.height()); InterpolateSize(minimum_size_rule, maximum_size_rule, min->height(),
preferred_height, *maximum_size.height());
} }
return gfx::Size(width, height); return gfx::Size(width, height);
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment