Home | History | Annotate | Download | only in layout
      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 
     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       spread_blank_space_(false) {
     23 }
     24 
     25 BoxLayout::~BoxLayout() {
     26 }
     27 
     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   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 
     84 gfx::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 
    100 int BoxLayout::GetPreferredHeightForWidth(View* host, int width) {
    101   int child_width = width - NonChildSize(host).width();
    102   return GetPreferredSizeForChildWidth(host, child_width).height();
    103 }
    104 
    105 gfx::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 
    150 gfx::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