• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
10 namespace views {
11 
BoxLayout(BoxLayout::Orientation orientation,int inside_border_horizontal_spacing,int inside_border_vertical_spacing,int between_child_spacing)12 BoxLayout::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       main_axis_alignment_(MAIN_AXIS_ALIGNMENT_START) {
23 }
24 
~BoxLayout()25 BoxLayout::~BoxLayout() {
26 }
27 
Layout(View * host)28 void 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 
33   int padding = 0;
34   if (main_axis_alignment_ != MAIN_AXIS_ALIGNMENT_START) {
35     int total_main_axis_size = 0;
36     int num_visible = 0;
37     for (int i = 0; i < host->child_count(); ++i) {
38       View* child = host->child_at(i);
39       if (!child->visible())
40         continue;
41       if (orientation_ == kHorizontal) {
42         total_main_axis_size +=
43             child->GetPreferredSize().width() + between_child_spacing_;
44       } else {
45         total_main_axis_size += child->GetHeightForWidth(child_area.width()) +
46                                 between_child_spacing_;
47       }
48       ++num_visible;
49     }
50 
51     if (num_visible) {
52       total_main_axis_size -= between_child_spacing_;
53       int free_space = MainAxisSize(child_area) - total_main_axis_size;
54       int position = MainAxisPosition(child_area);
55       int size = MainAxisSize(child_area);
56       switch (main_axis_alignment_) {
57         case MAIN_AXIS_ALIGNMENT_FILL:
58           padding = std::max(free_space / num_visible, 0);
59           break;
60         case MAIN_AXIS_ALIGNMENT_CENTER:
61           position += free_space / 2;
62           size = total_main_axis_size;
63           break;
64         case MAIN_AXIS_ALIGNMENT_END:
65           position += free_space;
66           size = total_main_axis_size;
67           break;
68         default:
69           NOTREACHED();
70           break;
71       }
72       gfx::Rect new_child_area(child_area);
73       SetMainAxisPosition(position, &new_child_area);
74       SetMainAxisSize(size, &new_child_area);
75       child_area.Intersect(new_child_area);
76     }
77   }
78 
79   int x = child_area.x();
80   int y = child_area.y();
81   for (int i = 0; i < host->child_count(); ++i) {
82     View* child = host->child_at(i);
83     if (child->visible()) {
84       gfx::Rect bounds(x, y, child_area.width(), child_area.height());
85       if (orientation_ == kHorizontal) {
86         bounds.set_width(child->GetPreferredSize().width() + padding);
87         if (bounds.width() > 0)
88           x += bounds.width() + between_child_spacing_;
89       } else {
90         bounds.set_height(child->GetHeightForWidth(bounds.width()) + padding);
91         if (bounds.height() > 0)
92           y += bounds.height() + between_child_spacing_;
93       }
94       // Clamp child view bounds to |child_area|.
95       bounds.Intersect(child_area);
96       child->SetBoundsRect(bounds);
97     }
98   }
99 }
100 
GetPreferredSize(const View * host) const101 gfx::Size BoxLayout::GetPreferredSize(const View* host) const {
102   // Calculate the child views' preferred width.
103   int width = 0;
104   if (orientation_ == kVertical) {
105     for (int i = 0; i < host->child_count(); ++i) {
106       const View* child = host->child_at(i);
107       if (!child->visible())
108         continue;
109 
110       width = std::max(width, child->GetPreferredSize().width());
111     }
112   }
113 
114   return GetPreferredSizeForChildWidth(host, width);
115 }
116 
GetPreferredHeightForWidth(const View * host,int width) const117 int BoxLayout::GetPreferredHeightForWidth(const View* host, int width) const {
118   int child_width = width - NonChildSize(host).width();
119   return GetPreferredSizeForChildWidth(host, child_width).height();
120 }
121 
MainAxisSize(const gfx::Rect & child_area) const122 int BoxLayout::MainAxisSize(const gfx::Rect& child_area) const {
123   return orientation_ == kHorizontal ? child_area.width() : child_area.height();
124 }
125 
MainAxisPosition(const gfx::Rect & child_area) const126 int BoxLayout::MainAxisPosition(const gfx::Rect& child_area) const {
127   return orientation_ == kHorizontal ? child_area.x() : child_area.y();
128 }
129 
SetMainAxisSize(int size,gfx::Rect * child_area) const130 void BoxLayout::SetMainAxisSize(int size, gfx::Rect* child_area) const {
131   if (orientation_ == kHorizontal)
132     child_area->set_width(size);
133   else
134     child_area->set_height(size);
135 }
136 
SetMainAxisPosition(int position,gfx::Rect * child_area) const137 void BoxLayout::SetMainAxisPosition(int position, gfx::Rect* child_area) const {
138   if (orientation_ == kHorizontal)
139     child_area->set_x(position);
140   else
141     child_area->set_y(position);
142 }
143 
GetPreferredSizeForChildWidth(const View * host,int child_area_width) const144 gfx::Size BoxLayout::GetPreferredSizeForChildWidth(const View* host,
145                                                    int child_area_width) const {
146   gfx::Rect child_area_bounds;
147 
148   if (orientation_ == kHorizontal) {
149     // Horizontal layouts ignore |child_area_width|, meaning they mimic the
150     // default behavior of GridLayout::GetPreferredHeightForWidth().
151     // TODO(estade): fix this if it ever becomes a problem.
152     int position = 0;
153     for (int i = 0; i < host->child_count(); ++i) {
154       const View* child = host->child_at(i);
155       if (!child->visible())
156         continue;
157 
158       gfx::Size size(child->GetPreferredSize());
159       if (size.IsEmpty())
160         continue;
161 
162       gfx::Rect child_bounds(position, 0, size.width(), size.height());
163       child_area_bounds.Union(child_bounds);
164       position += size.width() + between_child_spacing_;
165     }
166   } else {
167     int height = 0;
168     for (int i = 0; i < host->child_count(); ++i) {
169       const View* child = host->child_at(i);
170       if (!child->visible())
171         continue;
172 
173       int extra_height = child->GetHeightForWidth(child_area_width);
174       // Only add |between_child_spacing_| if this is not the only child.
175       if (height != 0 && extra_height > 0)
176         height += between_child_spacing_;
177       height += extra_height;
178     }
179 
180     child_area_bounds.set_width(child_area_width);
181     child_area_bounds.set_height(height);
182   }
183 
184   gfx::Size non_child_size = NonChildSize(host);
185   return gfx::Size(child_area_bounds.width() + non_child_size.width(),
186                    child_area_bounds.height() + non_child_size.height());
187 }
188 
NonChildSize(const View * host) const189 gfx::Size BoxLayout::NonChildSize(const View* host) const {
190   gfx::Insets insets(host->GetInsets());
191   return gfx::Size(insets.width() + inside_border_insets_.width(),
192                    insets.height() + inside_border_insets_.height());
193 }
194 
195 } // namespace views
196