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 "chrome/browser/ui/views/location_bar/location_bar_layout.h"
6
7#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
8#include "ui/gfx/rect.h"
9#include "ui/views/view.h"
10
11
12// Description of a decoration to be added inside the location bar, either to
13// the left or to the right.
14struct LocationBarDecoration {
15  LocationBarDecoration(int y,
16                        int height,
17                        bool auto_collapse,
18                        double max_fraction,
19                        int edge_item_padding,
20                        int item_padding,
21                        views::View* view);
22
23  // The y position of the view inside its parent.
24  int y;
25
26  // The height of the view.
27  int height;
28
29  // True means that, if there is not enough available space in the location
30  // bar, the view will reduce its width either to its minimal width or to zero
31  // (making it invisible), whichever fits. If true, |max_fraction| must be 0.
32  bool auto_collapse;
33
34  // Used for resizeable decorations, indicates the maximum fraction of the
35  // location bar that can be taken by this decoration, 0 for non-resizable
36  // decorations. If non-zero, |auto_collapse| must be false.
37  double max_fraction;
38
39  // Padding to use if the decoration is the first element next to the edge.
40  int edge_item_padding;
41
42  // Padding to use if the decoration follows another decoration.
43  int item_padding;
44
45  views::View* view;
46
47  // The width computed by the layout process.
48  double computed_width;
49};
50
51LocationBarDecoration::LocationBarDecoration(int y,
52                                             int height,
53                                             bool auto_collapse,
54                                             double max_fraction,
55                                             int edge_item_padding,
56                                             int item_padding,
57                                             views::View* view)
58    : y(y),
59      height(height),
60      auto_collapse(auto_collapse),
61      max_fraction(max_fraction),
62      edge_item_padding(edge_item_padding),
63      item_padding(item_padding),
64      view(view),
65      computed_width(0) {
66  DCHECK((max_fraction == 0.0) || (!auto_collapse && (max_fraction > 0.0)));
67}
68
69
70// LocationBarLayout ---------------------------------------------------------
71
72LocationBarLayout::LocationBarLayout(Position position, int item_edit_padding)
73    : position_(position),
74      item_edit_padding_(item_edit_padding) {
75}
76
77
78LocationBarLayout::~LocationBarLayout() {
79}
80
81void LocationBarLayout::AddDecoration(int y,
82                                      int height,
83                                      bool auto_collapse,
84                                      double max_fraction,
85                                      int edge_item_padding,
86                                      int item_padding,
87                                      views::View* view) {
88  decorations_.push_back(new LocationBarDecoration(
89      y, height, auto_collapse, max_fraction, edge_item_padding, item_padding,
90      view));
91}
92
93void LocationBarLayout::AddDecoration(int y,
94                                      int height,
95                                      views::View* view) {
96  decorations_.push_back(new LocationBarDecoration(
97      y, height, false, 0, LocationBarView::kItemPadding,
98      LocationBarView::kItemPadding, view));
99}
100
101void LocationBarLayout::LayoutPass1(int* entry_width) {
102  bool first_item = true;
103  for (Decorations::iterator i(decorations_.begin()); i != decorations_.end();
104       ++i) {
105    // Autocollapsing decorations are ignored in this pass.
106    if (!(*i)->auto_collapse) {
107      *entry_width -=
108          (first_item ? (*i)->edge_item_padding : (*i)->item_padding);
109    }
110    first_item = false;
111    // Resizing decorations are ignored in this pass.
112    if (!(*i)->auto_collapse && ((*i)->max_fraction == 0.0)) {
113      (*i)->computed_width = (*i)->view->GetPreferredSize().width();
114      *entry_width -= (*i)->computed_width;
115    }
116  }
117  *entry_width -= item_edit_padding_;
118}
119
120void LocationBarLayout::LayoutPass2(int *entry_width) {
121  for (Decorations::iterator i(decorations_.begin()); i != decorations_.end();
122       ++i) {
123    if ((*i)->max_fraction > 0.0) {
124      int max_width = static_cast<int>(*entry_width * (*i)->max_fraction);
125      (*i)->computed_width =
126          std::min((*i)->view->GetPreferredSize().width(),
127                   std::max((*i)->view->GetMinimumSize().width(), max_width));
128      *entry_width -= (*i)->computed_width;
129    }
130  }
131}
132
133void LocationBarLayout::LayoutPass3(gfx::Rect* bounds, int* available_width) {
134  bool first_visible = true;
135  for (Decorations::iterator i(decorations_.begin()); i != decorations_.end();
136       ++i) {
137    // Collapse decorations if needed.
138    if ((*i)->auto_collapse) {
139      int padding =
140          (first_visible ? (*i)->edge_item_padding : (*i)->item_padding);
141      // Try preferred size, if it fails try minimum size, if it fails collapse.
142      (*i)->computed_width = (*i)->view->GetPreferredSize().width();
143      if ((*i)->computed_width + padding > *available_width)
144        (*i)->computed_width = (*i)->view->GetMinimumSize().width();
145      if ((*i)->computed_width + padding > *available_width) {
146        (*i)->computed_width = 0;
147        (*i)->view->SetVisible(false);
148      } else {
149        (*i)->view->SetVisible(true);
150        (*available_width) -= (*i)->computed_width + padding;
151      }
152    } else {
153      (*i)->view->SetVisible(true);
154    }
155
156    // Layout visible decorations.
157    if (!(*i)->view->visible())
158      continue;
159    int padding =
160        (first_visible ? (*i)->edge_item_padding : (*i)->item_padding);
161    first_visible = false;
162    int x = (position_ == LEFT_EDGE) ? (bounds->x() + padding) :
163        (bounds->right() - padding - (*i)->computed_width);
164    (*i)->view->SetBounds(x, (*i)->y, (*i)->computed_width, (*i)->height);
165    bounds->set_width(bounds->width() - padding - (*i)->computed_width);
166    if (position_ == LEFT_EDGE)
167      bounds->set_x(bounds->x() + padding + (*i)->computed_width);
168  }
169  bounds->set_width(bounds->width() - item_edit_padding_);
170  if (position_ == LEFT_EDGE)
171    bounds->set_x(bounds->x() + item_edit_padding_);
172}
173