1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ui/views/layout/box_layout.h"
6
7#include "ui/gfx/rect.h"
8#include "ui/views/view.h"
9
10namespace views {
11
12BoxLayout::BoxLayout(BoxLayout::Orientation orientation,
13                     int inside_border_horizontal_spacing,
14                     int inside_border_vertical_spacing,
15                     int between_child_spacing)
16    : orientation_(orientation),
17      inside_border_insets_(inside_border_vertical_spacing,
18                            inside_border_horizontal_spacing,
19                            inside_border_vertical_spacing,
20                            inside_border_horizontal_spacing),
21      between_child_spacing_(between_child_spacing),
22      spread_blank_space_(false) {
23}
24
25BoxLayout::~BoxLayout() {
26}
27
28void BoxLayout::Layout(View* host) {
29  gfx::Rect child_area(host->GetLocalBounds());
30  child_area.Inset(host->GetInsets());
31  child_area.Inset(inside_border_insets_);
32  int x = child_area.x();
33  int y = child_area.y();
34
35  int padding = 0;
36  if (spread_blank_space_) {
37    int total = 0;
38    int visible = 0;
39    for (int i = 0; i < host->child_count(); ++i) {
40      View* child = host->child_at(i);
41      if (!child->visible())
42        continue;
43      if (orientation_ == kHorizontal) {
44        total += child->GetPreferredSize().width() + between_child_spacing_;
45      } else {
46        total += child->GetHeightForWidth(child_area.width()) +
47            between_child_spacing_;
48      }
49      ++visible;
50    }
51
52    if (visible) {
53      total -= between_child_spacing_;
54      if (orientation_ == kHorizontal)
55        padding = (child_area.width() - total) / visible;
56      else
57        padding = (child_area.height() - total) / visible;
58
59      if (padding < 0)
60        padding = 0;
61    }
62  }
63
64  for (int i = 0; i < host->child_count(); ++i) {
65    View* child = host->child_at(i);
66    if (child->visible()) {
67      gfx::Rect bounds(x, y, child_area.width(), child_area.height());
68      if (orientation_ == kHorizontal) {
69        bounds.set_width(child->GetPreferredSize().width() + padding);
70        if (bounds.width() > 0)
71          x += bounds.width() + between_child_spacing_;
72      } else {
73        bounds.set_height(child->GetHeightForWidth(bounds.width()) + padding);
74        if (bounds.height() > 0)
75          y += bounds.height() + between_child_spacing_;
76      }
77      // Clamp child view bounds to |child_area|.
78      bounds.Intersect(child_area);
79      child->SetBoundsRect(bounds);
80    }
81  }
82}
83
84gfx::Size BoxLayout::GetPreferredSize(View* host) {
85  // Calculate the child views' preferred width.
86  int width = 0;
87  if (orientation_ == kVertical) {
88    for (int i = 0; i < host->child_count(); ++i) {
89      View* child = host->child_at(i);
90      if (!child->visible())
91        continue;
92
93      width = std::max(width, child->GetPreferredSize().width());
94    }
95  }
96
97  return GetPreferredSizeForChildWidth(host, width);
98}
99
100int BoxLayout::GetPreferredHeightForWidth(View* host, int width) {
101  int child_width = width - NonChildSize(host).width();
102  return GetPreferredSizeForChildWidth(host, child_width).height();
103}
104
105gfx::Size BoxLayout::GetPreferredSizeForChildWidth(View* host,
106                                                   int child_area_width) {
107  gfx::Rect child_area_bounds;
108
109  if (orientation_ == kHorizontal) {
110    // Horizontal layouts ignore |child_area_width|, meaning they mimic the
111    // default behavior of GridLayout::GetPreferredHeightForWidth().
112    // TODO(estade): fix this if it ever becomes a problem.
113    int position = 0;
114    for (int i = 0; i < host->child_count(); ++i) {
115      View* child = host->child_at(i);
116      if (!child->visible())
117        continue;
118
119      gfx::Size size(child->GetPreferredSize());
120      if (size.IsEmpty())
121        continue;
122
123      gfx::Rect child_bounds(position, 0, size.width(), size.height());
124      child_area_bounds.Union(child_bounds);
125      position += size.width() + between_child_spacing_;
126    }
127  } else {
128    int height = 0;
129    for (int i = 0; i < host->child_count(); ++i) {
130      View* child = host->child_at(i);
131      if (!child->visible())
132        continue;
133
134      int extra_height = child->GetHeightForWidth(child_area_width);
135      // Only add |between_child_spacing_| if this is not the only child.
136      if (height != 0 && extra_height > 0)
137        height += between_child_spacing_;
138      height += extra_height;
139    }
140
141    child_area_bounds.set_width(child_area_width);
142    child_area_bounds.set_height(height);
143  }
144
145  gfx::Size non_child_size = NonChildSize(host);
146  return gfx::Size(child_area_bounds.width() + non_child_size.width(),
147                   child_area_bounds.height() + non_child_size.height());
148}
149
150gfx::Size BoxLayout::NonChildSize(View* host) {
151  gfx::Insets insets(host->GetInsets());
152  return gfx::Size(insets.width() + inside_border_insets_.width(),
153                   insets.height() + inside_border_insets_.height());
154}
155
156} // namespace views
157