Commit 9cfa1257 authored by calamity@chromium.org's avatar calamity@chromium.org

Add Flex to views::BoxLayout.

This CL adds a per-view flex property to BoxLayout. The flex property functions
similarly to the CSS flexbox concept of flex, using the child view's preferred
size as the flex basis and then adding or removing space within each flexed
so that all views fit within the parent.

This CL also removes MAIN_AXIS_ALIGNMENT_FILL as it is superceded by
SetDefaultFlex().

BUG=386475

Review URL: https://codereview.chromium.org/360213002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@285514 0039d316-1c4b-4281-b951-d872f2087c98
parent ee8d3b00
...@@ -336,7 +336,7 @@ void NetworkStateListDetailedView::CreateNetworkExtra() { ...@@ -336,7 +336,7 @@ void NetworkStateListDetailedView::CreateNetworkExtra() {
kTrayMenuBottomRowPadding, kTrayMenuBottomRowPadding,
kTrayMenuBottomRowPadding, kTrayMenuBottomRowPadding,
kTrayMenuBottomRowPaddingBetweenItems); kTrayMenuBottomRowPaddingBetweenItems);
layout->set_main_axis_alignment(views::BoxLayout::MAIN_AXIS_ALIGNMENT_FILL); layout->SetDefaultFlex(1);
bottom_row->SetLayoutManager(layout); bottom_row->SetLayoutManager(layout);
if (list_type_ != LIST_TYPE_VPN) { if (list_type_ != LIST_TYPE_VPN) {
......
...@@ -53,7 +53,7 @@ class TrayPopupItemContainer : public views::View { ...@@ -53,7 +53,7 @@ class TrayPopupItemContainer : public views::View {
} }
views::BoxLayout* layout = new views::BoxLayout( views::BoxLayout* layout = new views::BoxLayout(
views::BoxLayout::kVertical, 0, 0, 0); views::BoxLayout::kVertical, 0, 0, 0);
layout->set_main_axis_alignment(views::BoxLayout::MAIN_AXIS_ALIGNMENT_FILL); layout->SetDefaultFlex(1);
SetLayoutManager(layout); SetLayoutManager(layout);
SetPaintToLayer(view->layer() != NULL); SetPaintToLayer(view->layer() != NULL);
if (view->layer()) if (view->layer())
......
...@@ -276,7 +276,7 @@ void TrayBackgroundView::TrayContainer::UpdateLayout() { ...@@ -276,7 +276,7 @@ void TrayBackgroundView::TrayContainer::UpdateLayout() {
views::BoxLayout* layout = views::BoxLayout* layout =
new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0); new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0);
layout->set_main_axis_alignment(views::BoxLayout::MAIN_AXIS_ALIGNMENT_FILL); layout->SetDefaultFlex(1);
views::View::SetLayoutManager(layout); views::View::SetLayoutManager(layout);
} else { } else {
SetBorder(views::Border::CreateEmptyBorder( SetBorder(views::Border::CreateEmptyBorder(
...@@ -287,7 +287,7 @@ void TrayBackgroundView::TrayContainer::UpdateLayout() { ...@@ -287,7 +287,7 @@ void TrayBackgroundView::TrayContainer::UpdateLayout() {
views::BoxLayout* layout = views::BoxLayout* layout =
new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0); new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0);
layout->set_main_axis_alignment(views::BoxLayout::MAIN_AXIS_ALIGNMENT_FILL); layout->SetDefaultFlex(1);
views::View::SetLayoutManager(layout); views::View::SetLayoutManager(layout);
} }
PreferredSizeChanged(); PreferredSizeChanged();
......
...@@ -218,7 +218,7 @@ void AccessibilityDetailedView::AppendHelpEntries() { ...@@ -218,7 +218,7 @@ void AccessibilityDetailedView::AppendHelpEntries() {
kTrayMenuBottomRowPadding, kTrayMenuBottomRowPadding,
kTrayMenuBottomRowPadding, kTrayMenuBottomRowPadding,
kTrayMenuBottomRowPaddingBetweenItems); kTrayMenuBottomRowPaddingBetweenItems);
layout->set_main_axis_alignment(views::BoxLayout::MAIN_AXIS_ALIGNMENT_FILL); layout->SetDefaultFlex(1);
bottom_row->SetLayoutManager(layout); bottom_row->SetLayoutManager(layout);
ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
......
...@@ -30,8 +30,7 @@ BottomHomeView::BottomHomeView(app_list::AppListViewDelegate* view_delegate) ...@@ -30,8 +30,7 @@ BottomHomeView::BottomHomeView(app_list::AppListViewDelegate* view_delegate)
views::BoxLayout* items_layout = new views::BoxLayout( views::BoxLayout* items_layout = new views::BoxLayout(
views::BoxLayout::kHorizontal, 0, 0, 0); views::BoxLayout::kHorizontal, 0, 0, 0);
items_layout->set_main_axis_alignment( items_layout->SetDefaultFlex(1);
views::BoxLayout::MAIN_AXIS_ALIGNMENT_FILL);
items_container->SetLayoutManager(items_layout); items_container->SetLayoutManager(items_layout);
for (size_t i = 0; i < top_level->item_count(); ++i) { for (size_t i = 0; i < top_level->item_count(); ++i) {
app_list::TileItemView* tile_item_view = new app_list::TileItemView(); app_list::TileItemView* tile_item_view = new app_list::TileItemView();
......
...@@ -49,7 +49,7 @@ MessageCenterWidgetDelegate::MessageCenterWidgetDelegate( ...@@ -49,7 +49,7 @@ MessageCenterWidgetDelegate::MessageCenterWidgetDelegate(
views::BoxLayout* layout = views::BoxLayout* layout =
new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0); new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0);
layout->set_main_axis_alignment(views::BoxLayout::MAIN_AXIS_ALIGNMENT_FILL); layout->SetDefaultFlex(1);
SetLayoutManager(layout); SetLayoutManager(layout);
AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE)); AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
......
...@@ -181,7 +181,7 @@ MessageListView::MessageListView(MessageCenterView* message_center_view, ...@@ -181,7 +181,7 @@ MessageListView::MessageListView(MessageCenterView* message_center_view,
weak_ptr_factory_(this) { weak_ptr_factory_(this) {
views::BoxLayout* layout = views::BoxLayout* layout =
new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1); new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1);
layout->set_main_axis_alignment(views::BoxLayout::MAIN_AXIS_ALIGNMENT_FILL); layout->SetDefaultFlex(1);
SetLayoutManager(layout); SetLayoutManager(layout);
// Set the margin to 0 for the layout. BoxLayout assumes the same margin // Set the margin to 0 for the layout. BoxLayout assumes the same margin
......
...@@ -389,7 +389,7 @@ gfx::Insets TrayBubbleView::GetBorderInsets() const { ...@@ -389,7 +389,7 @@ gfx::Insets TrayBubbleView::GetBorderInsets() const {
void TrayBubbleView::Init() { void TrayBubbleView::Init() {
BoxLayout* layout = new BottomAlignedBoxLayout(this); BoxLayout* layout = new BottomAlignedBoxLayout(this);
layout->set_main_axis_alignment(views::BoxLayout::MAIN_AXIS_ALIGNMENT_FILL); layout->SetDefaultFlex(1);
SetLayoutManager(layout); SetLayoutManager(layout);
} }
......
...@@ -20,88 +20,138 @@ BoxLayout::BoxLayout(BoxLayout::Orientation orientation, ...@@ -20,88 +20,138 @@ BoxLayout::BoxLayout(BoxLayout::Orientation orientation,
inside_border_horizontal_spacing), inside_border_horizontal_spacing),
between_child_spacing_(between_child_spacing), between_child_spacing_(between_child_spacing),
main_axis_alignment_(MAIN_AXIS_ALIGNMENT_START), main_axis_alignment_(MAIN_AXIS_ALIGNMENT_START),
cross_axis_alignment_(CROSS_AXIS_ALIGNMENT_STRETCH) { cross_axis_alignment_(CROSS_AXIS_ALIGNMENT_STRETCH),
default_flex_(0),
host_(NULL) {
} }
BoxLayout::~BoxLayout() { BoxLayout::~BoxLayout() {
} }
void BoxLayout::SetFlexForView(const View* view, int flex_weight) {
DCHECK(host_);
DCHECK(view);
DCHECK_EQ(host_, view->parent());
DCHECK_GE(flex_weight, 0);
flex_map_[view] = flex_weight;
}
void BoxLayout::ClearFlexForView(const View* view) {
DCHECK(view);
flex_map_.erase(view);
}
void BoxLayout::SetDefaultFlex(int default_flex) {
DCHECK_GE(default_flex, 0);
default_flex_ = default_flex;
}
void BoxLayout::Layout(View* host) { void BoxLayout::Layout(View* host) {
DCHECK_EQ(host_, host);
gfx::Rect child_area(host->GetLocalBounds()); gfx::Rect child_area(host->GetLocalBounds());
child_area.Inset(host->GetInsets()); child_area.Inset(host->GetInsets());
child_area.Inset(inside_border_insets_); child_area.Inset(inside_border_insets_);
int padding = 0; int total_main_axis_size = 0;
if (main_axis_alignment_ != MAIN_AXIS_ALIGNMENT_START) { int num_visible = 0;
int total_main_axis_size = 0; int flex_sum = 0;
int num_visible = 0; // Calculate the total size of children in the main axis.
for (int i = 0; i < host->child_count(); ++i) { for (int i = 0; i < host->child_count(); ++i) {
View* child = host->child_at(i); View* child = host->child_at(i);
if (!child->visible()) if (!child->visible())
continue; continue;
total_main_axis_size += MainAxisSizeForView(child, child_area.width()) + total_main_axis_size +=
between_child_spacing_; MainAxisSizeForView(child, child_area.width()) + between_child_spacing_;
++num_visible; ++num_visible;
} flex_sum += GetFlexForView(child);
}
if (!num_visible)
return;
if (num_visible) { total_main_axis_size -= between_child_spacing_;
total_main_axis_size -= between_child_spacing_; // Free space can be negative indicating that the views want to overflow.
int free_space = MainAxisSize(child_area) - total_main_axis_size; int main_free_space = MainAxisSize(child_area) - total_main_axis_size;
int position = MainAxisPosition(child_area); {
int size = MainAxisSize(child_area); int position = MainAxisPosition(child_area);
int size = MainAxisSize(child_area);
if (!flex_sum) {
switch (main_axis_alignment_) { switch (main_axis_alignment_) {
case MAIN_AXIS_ALIGNMENT_FILL: case MAIN_AXIS_ALIGNMENT_START:
padding = std::max(free_space / num_visible, 0);
break; break;
case MAIN_AXIS_ALIGNMENT_CENTER: case MAIN_AXIS_ALIGNMENT_CENTER:
position += free_space / 2; position += main_free_space / 2;
size = total_main_axis_size; size = total_main_axis_size;
break; break;
case MAIN_AXIS_ALIGNMENT_END: case MAIN_AXIS_ALIGNMENT_END:
position += free_space; position += main_free_space;
size = total_main_axis_size; size = total_main_axis_size;
break; break;
default: default:
NOTREACHED(); NOTREACHED();
break; break;
} }
gfx::Rect new_child_area(child_area);
SetMainAxisPosition(position, &new_child_area);
SetMainAxisSize(size, &new_child_area);
child_area.Intersect(new_child_area);
} }
gfx::Rect new_child_area(child_area);
SetMainAxisPosition(position, &new_child_area);
SetMainAxisSize(size, &new_child_area);
child_area.Intersect(new_child_area);
} }
int main_position = MainAxisPosition(child_area); int main_position = MainAxisPosition(child_area);
int total_padding = 0;
int current_flex = 0;
for (int i = 0; i < host->child_count(); ++i) { for (int i = 0; i < host->child_count(); ++i) {
View* child = host->child_at(i); View* child = host->child_at(i);
if (child->visible()) { if (!child->visible())
gfx::Rect bounds(child_area); continue;
SetMainAxisPosition(main_position, &bounds);
if (cross_axis_alignment_ != CROSS_AXIS_ALIGNMENT_STRETCH) { // Calculate cross axis size.
int free_space = CrossAxisSize(bounds) - CrossAxisSizeForView(child); gfx::Rect bounds(child_area);
int position = CrossAxisPosition(bounds); SetMainAxisPosition(main_position, &bounds);
if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_CENTER) { if (cross_axis_alignment_ != CROSS_AXIS_ALIGNMENT_STRETCH) {
position += free_space / 2; int free_space = CrossAxisSize(bounds) - CrossAxisSizeForView(child);
} else if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_END) { int position = CrossAxisPosition(bounds);
position += free_space; if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_CENTER) {
} position += free_space / 2;
SetCrossAxisPosition(position, &bounds); } else if (cross_axis_alignment_ == CROSS_AXIS_ALIGNMENT_END) {
SetCrossAxisSize(CrossAxisSizeForView(child), &bounds); position += free_space;
} }
int child_main_axis_size = MainAxisSizeForView(child, child_area.width()); SetCrossAxisPosition(position, &bounds);
SetMainAxisSize(child_main_axis_size + padding, &bounds); SetCrossAxisSize(CrossAxisSizeForView(child), &bounds);
if (MainAxisSize(bounds) > 0)
main_position += MainAxisSize(bounds) + between_child_spacing_;
// Clamp child view bounds to |child_area|.
bounds.Intersect(child_area);
child->SetBoundsRect(bounds);
} }
// Calculate flex padding.
int current_padding = 0;
if (GetFlexForView(child) > 0) {
current_flex += GetFlexForView(child);
int quot = (main_free_space * current_flex) / flex_sum;
int rem = (main_free_space * current_flex) % flex_sum;
current_padding = quot - total_padding;
// Use the current remainder to round to the nearest pixel.
if (std::abs(rem) * 2 >= flex_sum)
current_padding += main_free_space > 0 ? 1 : -1;
total_padding += current_padding;
}
// Set main axis size.
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_;
// Clamp child view bounds to |child_area|.
bounds.Intersect(child_area);
child->SetBoundsRect(bounds);
} }
// Flex views should have grown/shrunk to consume all free space.
if (flex_sum)
DCHECK_EQ(total_padding, main_free_space);
} }
gfx::Size BoxLayout::GetPreferredSize(const View* host) const { gfx::Size BoxLayout::GetPreferredSize(const View* host) const {
DCHECK_EQ(host_, host);
// Calculate the child views' preferred width. // Calculate the child views' preferred width.
int width = 0; int width = 0;
if (orientation_ == kVertical) { if (orientation_ == kVertical) {
...@@ -118,10 +168,34 @@ gfx::Size BoxLayout::GetPreferredSize(const View* host) const { ...@@ -118,10 +168,34 @@ gfx::Size BoxLayout::GetPreferredSize(const View* host) const {
} }
int BoxLayout::GetPreferredHeightForWidth(const View* host, int width) const { int BoxLayout::GetPreferredHeightForWidth(const View* host, int width) const {
DCHECK_EQ(host_, host);
int child_width = width - NonChildSize(host).width(); int child_width = width - NonChildSize(host).width();
return GetPreferredSizeForChildWidth(host, child_width).height(); return GetPreferredSizeForChildWidth(host, child_width).height();
} }
void BoxLayout::Installed(View* host) {
DCHECK(!host_);
host_ = host;
}
void BoxLayout::Uninstalled(View* host) {
DCHECK_EQ(host_, host);
host_ = NULL;
flex_map_.clear();
}
void BoxLayout::ViewRemoved(View* host, View* view) {
ClearFlexForView(view);
}
int BoxLayout::GetFlexForView(const View* view) const {
std::map<const View*, int>::const_iterator it = flex_map_.find(view);
if (it == flex_map_.end())
return default_flex_;
return it->second;
}
int BoxLayout::MainAxisSize(const gfx::Rect& rect) const { int BoxLayout::MainAxisSize(const gfx::Rect& rect) const {
return orientation_ == kHorizontal ? rect.width() : rect.height(); return orientation_ == kHorizontal ? rect.width() : rect.height();
} }
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#ifndef UI_VIEWS_LAYOUT_BOX_LAYOUT_H_ #ifndef UI_VIEWS_LAYOUT_BOX_LAYOUT_H_
#define UI_VIEWS_LAYOUT_BOX_LAYOUT_H_ #define UI_VIEWS_LAYOUT_BOX_LAYOUT_H_
#include <map>
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "ui/gfx/insets.h" #include "ui/gfx/insets.h"
...@@ -38,11 +40,6 @@ class VIEWS_EXPORT BoxLayout : public LayoutManager { ...@@ -38,11 +40,6 @@ class VIEWS_EXPORT BoxLayout : public LayoutManager {
MAIN_AXIS_ALIGNMENT_START, MAIN_AXIS_ALIGNMENT_START,
MAIN_AXIS_ALIGNMENT_CENTER, MAIN_AXIS_ALIGNMENT_CENTER,
MAIN_AXIS_ALIGNMENT_END, MAIN_AXIS_ALIGNMENT_END,
// This distributes extra space among the child views. This increases the
// size of child views along the main axis rather than the space between
// them.
MAIN_AXIS_ALIGNMENT_FILL,
// TODO(calamity): Add MAIN_AXIS_ALIGNMENT_JUSTIFY which spreads blank space // TODO(calamity): Add MAIN_AXIS_ALIGNMENT_JUSTIFY which spreads blank space
// in-between the child views. // in-between the child views.
}; };
...@@ -80,13 +77,35 @@ class VIEWS_EXPORT BoxLayout : public LayoutManager { ...@@ -80,13 +77,35 @@ class VIEWS_EXPORT BoxLayout : public LayoutManager {
inside_border_insets_ = insets; inside_border_insets_ = insets;
} }
// Sets the flex weight for the given |view|. Using the preferred size as
// the basis, free space along the main axis is distributed to views in the
// ratio of their flex weights. Similarly, if the views will overflow the
// parent, space is subtracted in these ratios.
//
// A flex of 0 means this view is not resized. Flex values must not be
// negative.
void SetFlexForView(const View* view, int flex);
// Clears the flex for the given |view|, causing it to use the default
// flex.
void ClearFlexForView(const View* view);
// Sets the flex for views to use when none is specified.
void SetDefaultFlex(int default_flex);
// Overridden from views::LayoutManager: // Overridden from views::LayoutManager:
virtual void Installed(View* host) OVERRIDE;
virtual void Uninstalled(View* host) OVERRIDE;
virtual void ViewRemoved(View* host, View* view) OVERRIDE;
virtual void Layout(View* host) OVERRIDE; virtual void Layout(View* host) OVERRIDE;
virtual gfx::Size GetPreferredSize(const View* host) const OVERRIDE; virtual gfx::Size GetPreferredSize(const View* host) const OVERRIDE;
virtual int GetPreferredHeightForWidth(const View* host, virtual int GetPreferredHeightForWidth(const View* host,
int width) const OVERRIDE; int width) const OVERRIDE;
private: private:
// Returns the flex for the specified |view|.
int GetFlexForView(const View* view) const;
// Returns the size and position along the main axis of |rect|. // Returns the size and position along the main axis of |rect|.
int MainAxisSize(const gfx::Rect& rect) const; int MainAxisSize(const gfx::Rect& rect) const;
int MainAxisPosition(const gfx::Rect& rect) const; int MainAxisPosition(const gfx::Rect& rect) const;
...@@ -134,6 +153,15 @@ class VIEWS_EXPORT BoxLayout : public LayoutManager { ...@@ -134,6 +153,15 @@ class VIEWS_EXPORT BoxLayout : public LayoutManager {
// CROSS_AXIS_ALIGNMENT_STRETCH by default. // CROSS_AXIS_ALIGNMENT_STRETCH by default.
CrossAxisAlignment cross_axis_alignment_; CrossAxisAlignment cross_axis_alignment_;
// A map of views to their flex weights.
std::map<const View*, int> flex_map_;
// The flex weight for views if none is set. Defaults to 0.
int default_flex_;
// The view that this BoxLayout is managing the layout for.
views::View* host_;
DISALLOW_IMPLICIT_CONSTRUCTORS(BoxLayout); DISALLOW_IMPLICIT_CONSTRUCTORS(BoxLayout);
}; };
......
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