Commit 3c72a0bc authored by Dana Fried's avatar Dana Fried Committed by Commit Bot

Make FlexLayout inherit LayoutManagerBase.

As a proof of concept for the separation of layout generation and
application, this CL reimplements FlexLayout using GetProposedLayout(),
with significant code savings. The new layout has almost the same
programming interface, and passes all existing unit tests.

(Note that this also removes some of the explicit caching in
FlexLayout but it can always be added back in later if we determine
it's needed.)

For more information on the separation, see this upstream CL:
https://chromium-review.googlesource.com/c/chromium/src/+/1653898

And this design doc (internal only for now):
https://docs.google.com/document/d/1fZOg120fswUdaV7tfZh8cXIlcvAL1Gg70GVrsQVXl3g/edit

Next steps:
 - (optional) Expand FlexLayout with some features that make it
   friendlier to use, especially in interpolating layouts.
 - Implement AnimatingLayout (extending LayoutManager) with basic layout
   animations, analogous to those supported by Cocoa's Core Animation
   library.
 - Move toolbar, bookmarks, and tab handles to an animating layout.

Follow-ups:
 - Combine LayoutManager and LayoutManagerBase
 - Update Box, Grid, and Fill layouts
 - Ensure other one-off layouts work with the new framework

Change-Id: I1bbd739e1b2f5e45d92c8d3a38473319c4b6806f
Bug: 898632
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1656249
Commit-Queue: Dana Fried <dfried@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Cr-Commit-Position: refs/heads/master@{#676086}
parent c8a8c39a
This diff is collapsed.
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <memory> #include <memory>
#include <set> #include <set>
#include <string> #include <string>
#include <vector>
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/macros.h" #include "base/macros.h"
...@@ -16,22 +17,16 @@ ...@@ -16,22 +17,16 @@
#include "ui/base/class_property.h" #include "ui/base/class_property.h"
#include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/insets.h"
#include "ui/views/layout/flex_layout_types.h" #include "ui/views/layout/flex_layout_types.h"
#include "ui/views/layout/layout_manager.h" #include "ui/views/layout/layout_manager_base.h"
#include "ui/views/views_export.h"
namespace gfx {
class Size;
} // namespace gfx
namespace views { namespace views {
class View; class View;
namespace internal { namespace internal {
struct ChildLayoutParams; class NormalizedSizeBounds;
class FlexLayoutInternal; }
} // namespace internal
class View;
// Provides CSS-like layout for a one-dimensional (vertical or horizontal) // Provides CSS-like layout for a one-dimensional (vertical or horizontal)
// arrangement of child views. Independent alignment can be specified for the // arrangement of child views. Independent alignment can be specified for the
...@@ -72,11 +67,11 @@ class View; ...@@ -72,11 +67,11 @@ class View;
// take up its preferred size in the layout. // take up its preferred size in the layout.
// //
// The core function of this class is contained in // The core function of this class is contained in
// GetPreferredSize(maximum_size) and Layout(host). In both cases, a layout will // GetPreferredSize(maximum_size) and Layout(). In both cases, a layout will
// be cached and typically not recalculated as long as none of the layout's // be cached and typically not recalculated as long as none of the layout's
// properties or the preferred size or visibility of any of its children has // properties or the preferred size or visibility of any of its children has
// changed. // changed.
class VIEWS_EXPORT FlexLayout : public LayoutManager { class VIEWS_EXPORT FlexLayout : public LayoutManagerBase {
public: public:
FlexLayout(); FlexLayout();
~FlexLayout() override; ~FlexLayout() override;
...@@ -92,12 +87,6 @@ class VIEWS_EXPORT FlexLayout : public LayoutManager { ...@@ -92,12 +87,6 @@ class VIEWS_EXPORT FlexLayout : public LayoutManager {
FlexLayout& SetInteriorMargin(const gfx::Insets& interior_margin); FlexLayout& SetInteriorMargin(const gfx::Insets& interior_margin);
FlexLayout& SetMinimumCrossAxisSize(int size); FlexLayout& SetMinimumCrossAxisSize(int size);
// Set whether a view should be excluded from the layout. Excluded views will
// be completely ignored and must be explicitly placed by the host view.
FlexLayout& SetViewExcluded(const View* view, bool excluded);
View* host() { return host_; }
const View* host() const { return host_; }
LayoutOrientation orientation() const { return orientation_; } LayoutOrientation orientation() const { return orientation_; }
bool collapse_margins() const { return collapse_margins_; } bool collapse_margins() const { return collapse_margins_; }
LayoutAlignment main_axis_alignment() const { return main_axis_alignment_; } LayoutAlignment main_axis_alignment() const { return main_axis_alignment_; }
...@@ -119,39 +108,86 @@ class VIEWS_EXPORT FlexLayout : public LayoutManager { ...@@ -119,39 +108,86 @@ class VIEWS_EXPORT FlexLayout : public LayoutManager {
return *this; return *this;
} }
bool IsViewExcluded(const View* view) const;
bool IsHiddenByOwner(const View* view) const;
// Retrieve the preferred size for the control in the given bounds.
gfx::Size GetPreferredSize(const SizeBounds& maximum_size) const;
protected: protected:
// views::LayoutManager: // LayoutManagerBase:
void Installed(View* host) override; ProposedLayout CalculateProposedLayout(
void InvalidateLayout() override; const SizeBounds& size_bounds) const override;
void ViewAdded(View* host, View* view) override;
void ViewRemoved(View* host, View* view) override;
void ViewVisibilitySet(View* host, View* view, bool visible) override;
void Layout(View* host) override;
gfx::Size GetPreferredSize(const View* host) const override;
gfx::Size GetMinimumSize(const View* host) const override;
int GetPreferredHeightForWidth(const View* host, int width) const override;
private: private:
friend class internal::FlexLayoutInternal; struct ChildLayoutParams;
class ChildViewSpacing;
struct FlexLayoutData;
class PropertyHandler : public ui::PropertyHandler { class PropertyHandler : public ui::PropertyHandler {
public: public:
explicit PropertyHandler(internal::FlexLayoutInternal* layout); explicit PropertyHandler(FlexLayout* layout);
protected: protected:
// ui::PropertyHandler: // ui::PropertyHandler:
void AfterPropertyChange(const void* key, int64_t old_value) override; void AfterPropertyChange(const void* key, int64_t old_value) override;
private: private:
internal::FlexLayoutInternal* const layout_; FlexLayout* const layout_;
}; };
// Maps a flex order (lower = allocated first, and therefore higher priority)
// to the indices of child views within that order that can flex.
// See FlexSpecification::order().
using FlexOrderToViewIndexMap = std::map<int, std::vector<size_t>>;
// Calculates a margin between two child views based on each's margin and any
// internal padding present in one or both elements. Uses properties of the
// layout, like whether adjacent margins should be collapsed.
int CalculateMargin(int margin1, int margin2, int internal_padding) const;
// Calculates the cross-layout space available to a view based on the
// available space and margins.
base::Optional<int> GetAvailableCrossAxisSize(
const FlexLayoutData& layout,
size_t child_index,
const internal::NormalizedSizeBounds& bounds) const;
// Calculates the preferred spacing between two child views, or between a
// view edge and the first or last visible child views.
int CalculateChildSpacing(const FlexLayoutData& layout,
base::Optional<size_t> child1_index,
base::Optional<size_t> child2_index) const;
// Calculates the position of each child view and the size of the overall
// layout based on tentative visibilities and sizes for each child.
void UpdateLayoutFromChildren(const internal::NormalizedSizeBounds& bounds,
FlexLayoutData* data,
ChildViewSpacing* child_spacing) const;
// Applies flex rules to each view in a layout, updating |data| and
// |child_spacing|.
//
// If |expandable_views| is specified, any view requesting more than its
// preferred size will be clamped to its preferred size and be added to
// |expandable_views| for later processing after all other flex space has been
// allocated.
//
// Typically, this method will be called once with |expandable_views| set and
// then again with it null to allocate the remaining space.
void AllocateFlexSpace(
const internal::NormalizedSizeBounds& bounds,
const FlexOrderToViewIndexMap& order_to_index,
FlexLayoutData* data,
ChildViewSpacing* child_spacing,
FlexOrderToViewIndexMap* expandable_views = nullptr) const;
// Fills out the child entries for |data| and generates some initial size
// and visibility data, and stores off information about which views can
// expand in |flex_order_to_index|.
void InitializeChildData(const internal::NormalizedSizeBounds& bounds,
FlexLayoutData* data,
FlexOrderToViewIndexMap* flex_order_to_index) const;
// Caclulates the child bounds (in screen coordinates) for each visible child
// in the layout.
void CalculateChildBounds(const SizeBounds& size_bounds,
FlexLayoutData* data) const;
// Gets the default value for a particular layout property, which will be used // Gets the default value for a particular layout property, which will be used
// if the property is not set on a child view being laid out (e.g. // if the property is not set on a child view being laid out (e.g.
// kMarginsKey). // kMarginsKey).
...@@ -186,19 +222,9 @@ class VIEWS_EXPORT FlexLayout : public LayoutManager { ...@@ -186,19 +222,9 @@ class VIEWS_EXPORT FlexLayout : public LayoutManager {
// The minimum cross axis size for the layout. // The minimum cross axis size for the layout.
int minimum_cross_axis_size_ = 0; int minimum_cross_axis_size_ = 0;
// Tracks layout-specific information about child views.
std::map<const View*, internal::ChildLayoutParams> child_params_;
// The view that this FlexLayout is managing the layout for.
views::View* host_ = nullptr;
// Internal data used to cache layout information, etc. All definitions and
// data are module-private.
std::unique_ptr<internal::FlexLayoutInternal> internal_;
// Default properties for any views that don't have them explicitly set for // Default properties for any views that don't have them explicitly set for
// this layout. // this layout.
PropertyHandler layout_defaults_; PropertyHandler layout_defaults_{this};
DISALLOW_COPY_AND_ASSIGN(FlexLayout); DISALLOW_COPY_AND_ASSIGN(FlexLayout);
}; };
......
...@@ -142,6 +142,10 @@ void NormalizedSizeBounds::Expand(int main, int cross) { ...@@ -142,6 +142,10 @@ void NormalizedSizeBounds::Expand(int main, int cross) {
cross_ = std::max(0, *cross_ + cross); cross_ = std::max(0, *cross_ + cross);
} }
void NormalizedSizeBounds::Inset(const NormalizedInsets& insets) {
Expand(-insets.main_size(), -insets.cross_size());
}
bool NormalizedSizeBounds::operator==(const NormalizedSizeBounds& other) const { bool NormalizedSizeBounds::operator==(const NormalizedSizeBounds& other) const {
return main_ == other.main_ && cross_ == other.cross_; return main_ == other.main_ && cross_ == other.cross_;
} }
......
...@@ -160,6 +160,7 @@ class NormalizedSizeBounds { ...@@ -160,6 +160,7 @@ class NormalizedSizeBounds {
void set_cross(const base::Optional<int>& cross) { cross_ = cross; } void set_cross(const base::Optional<int>& cross) { cross_ = cross; }
void Expand(int main, int cross); void Expand(int main, int cross);
void Inset(const NormalizedInsets& insets);
bool operator==(const NormalizedSizeBounds& other) const; bool operator==(const NormalizedSizeBounds& other) const;
bool operator!=(const NormalizedSizeBounds& other) const; bool operator!=(const NormalizedSizeBounds& other) const;
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <stddef.h> #include <stddef.h>
#include <algorithm> #include <algorithm>
#include <utility>
#include <vector> #include <vector>
#include "base/bind.h" #include "base/bind.h"
...@@ -315,7 +316,6 @@ TEST_F(FlexLayoutTest, Layout_VisibilitySetBeforeAdd) { ...@@ -315,7 +316,6 @@ TEST_F(FlexLayoutTest, Layout_VisibilitySetBeforeAdd) {
View* child3 = AddChild(kChild3Size); View* child3 = AddChild(kChild3Size);
host_->Layout(); host_->Layout();
EXPECT_EQ(true, layout_->IsHiddenByOwner(child2));
EXPECT_FALSE(child2->GetVisible()); EXPECT_FALSE(child2->GetVisible());
EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds()); EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds());
EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds()); EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds());
...@@ -324,7 +324,6 @@ TEST_F(FlexLayoutTest, Layout_VisibilitySetBeforeAdd) { ...@@ -324,7 +324,6 @@ TEST_F(FlexLayoutTest, Layout_VisibilitySetBeforeAdd) {
// This should have no additional effect since the child is already invisible. // This should have no additional effect since the child is already invisible.
child2->SetVisible(false); child2->SetVisible(false);
host_->Layout(); host_->Layout();
EXPECT_EQ(true, layout_->IsHiddenByOwner(child2));
EXPECT_FALSE(child2->GetVisible()); EXPECT_FALSE(child2->GetVisible());
EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds()); EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds());
EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds()); EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds());
...@@ -334,7 +333,6 @@ TEST_F(FlexLayoutTest, Layout_VisibilitySetBeforeAdd) { ...@@ -334,7 +333,6 @@ TEST_F(FlexLayoutTest, Layout_VisibilitySetBeforeAdd) {
host_->Layout(); host_->Layout();
std::vector<Rect> expected{Rect(6, 5, 12, 10), Rect(18, 5, 13, 11), std::vector<Rect> expected{Rect(6, 5, 12, 10), Rect(18, 5, 13, 11),
Rect(31, 5, 17, 13)}; Rect(31, 5, 17, 13)};
EXPECT_EQ(false, layout_->IsHiddenByOwner(child2));
EXPECT_TRUE(child2->GetVisible()); EXPECT_TRUE(child2->GetVisible());
EXPECT_EQ(expected, GetChildBounds()); EXPECT_EQ(expected, GetChildBounds());
EXPECT_EQ(Size(57, 25), host_->GetPreferredSize()); EXPECT_EQ(Size(57, 25), host_->GetPreferredSize());
...@@ -351,7 +349,6 @@ TEST_F(FlexLayoutTest, Layout_VisibilitySetAfterAdd) { ...@@ -351,7 +349,6 @@ TEST_F(FlexLayoutTest, Layout_VisibilitySetAfterAdd) {
child2->SetVisible(false); child2->SetVisible(false);
host_->Layout(); host_->Layout();
EXPECT_EQ(true, layout_->IsHiddenByOwner(child2));
EXPECT_FALSE(child2->GetVisible()); EXPECT_FALSE(child2->GetVisible());
EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds()); EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds());
EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds()); EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds());
...@@ -361,7 +358,6 @@ TEST_F(FlexLayoutTest, Layout_VisibilitySetAfterAdd) { ...@@ -361,7 +358,6 @@ TEST_F(FlexLayoutTest, Layout_VisibilitySetAfterAdd) {
host_->Layout(); host_->Layout();
std::vector<Rect> expected{Rect(6, 5, 12, 10), Rect(18, 5, 13, 11), std::vector<Rect> expected{Rect(6, 5, 12, 10), Rect(18, 5, 13, 11),
Rect(31, 5, 17, 13)}; Rect(31, 5, 17, 13)};
EXPECT_EQ(false, layout_->IsHiddenByOwner(child2));
EXPECT_TRUE(child2->GetVisible()); EXPECT_TRUE(child2->GetVisible());
EXPECT_EQ(expected, GetChildBounds()); EXPECT_EQ(expected, GetChildBounds());
EXPECT_EQ(Size(57, 25), host_->GetPreferredSize()); EXPECT_EQ(Size(57, 25), host_->GetPreferredSize());
...@@ -381,7 +377,6 @@ TEST_F(FlexLayoutTest, ...@@ -381,7 +377,6 @@ TEST_F(FlexLayoutTest,
// Layout makes child view invisible due to flex rule. // Layout makes child view invisible due to flex rule.
host_->SetSize(Size(40, 25)); host_->SetSize(Size(40, 25));
host_->Layout(); host_->Layout();
EXPECT_EQ(false, layout_->IsHiddenByOwner(child2));
EXPECT_FALSE(child2->GetVisible()); EXPECT_FALSE(child2->GetVisible());
EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds()); EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds());
EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds()); EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds());
...@@ -390,11 +385,7 @@ TEST_F(FlexLayoutTest, ...@@ -390,11 +385,7 @@ TEST_F(FlexLayoutTest,
// Now we will make child explicitly hidden. // Now we will make child explicitly hidden.
child2->SetVisible(false); child2->SetVisible(false);
EXPECT_EQ(true, layout_->IsHiddenByOwner(child2));
// Layout is the same, but we should report that the view is hidden by owner.
host_->Layout(); host_->Layout();
EXPECT_EQ(true, layout_->IsHiddenByOwner(child2));
EXPECT_FALSE(child2->GetVisible()); EXPECT_FALSE(child2->GetVisible());
EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds()); EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds());
EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds()); EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds());
...@@ -410,20 +401,18 @@ TEST_F(FlexLayoutTest, Layout_Exlcude) { ...@@ -410,20 +401,18 @@ TEST_F(FlexLayoutTest, Layout_Exlcude) {
View* child2 = AddChild(kChild2Size); View* child2 = AddChild(kChild2Size);
const View* child3 = AddChild(kChild3Size); const View* child3 = AddChild(kChild3Size);
layout_->SetViewExcluded(child2, true); layout_->SetChildViewIgnoredByLayout(child2, true);
child2->SetBounds(3, 3, 3, 3); child2->SetBounds(3, 3, 3, 3);
host_->Layout(); host_->Layout();
EXPECT_EQ(true, layout_->IsViewExcluded(child2));
EXPECT_EQ(Rect(3, 3, 3, 3), child2->bounds()); EXPECT_EQ(Rect(3, 3, 3, 3), child2->bounds());
EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds()); EXPECT_EQ(Rect(6, 5, 12, 10), child1->bounds());
EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds()); EXPECT_EQ(Rect(18, 5, 17, 13), child3->bounds());
EXPECT_EQ(Size(44, 25), host_->GetPreferredSize()); EXPECT_EQ(Size(44, 25), host_->GetPreferredSize());
layout_->SetViewExcluded(child2, false); layout_->SetChildViewIgnoredByLayout(child2, false);
host_->Layout(); host_->Layout();
std::vector<Rect> expected{Rect(6, 5, 12, 10), Rect(18, 5, 13, 11), std::vector<Rect> expected{Rect(6, 5, 12, 10), Rect(18, 5, 13, 11),
Rect(31, 5, 17, 13)}; Rect(31, 5, 17, 13)};
EXPECT_EQ(false, layout_->IsViewExcluded(child2));
EXPECT_EQ(expected, GetChildBounds()); EXPECT_EQ(expected, GetChildBounds());
EXPECT_EQ(Size(57, 25), host_->GetPreferredSize()); EXPECT_EQ(Size(57, 25), host_->GetPreferredSize());
} }
......
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