location_bar_layout.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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 11namespace { 12 13enum DecorationType { 14 // Decoration is always visible. 15 NORMAL = 0, 16 // If there is not enough available space in the location bar, the decoration 17 // will reduce its width either to its minimal width or to zero (making it 18 // invisible), whichever fits. |LocationBarDecoration::max_fraction| must be 19 // 0. 20 AUTO_COLLAPSE, 21 // Decoration is a separator, only visible if it's not leading, or not 22 // trailing, or not next to another separator. 23 SEPARATOR, 24}; 25 26} // namespace 27 28 29// Description of a decoration to be added inside the location bar, either to 30// the left or to the right. 31struct LocationBarDecoration { 32 LocationBarDecoration(DecorationType type, 33 int y, 34 int height, 35 double max_fraction, 36 int edge_item_padding, 37 int item_padding, 38 int builtin_padding, 39 views::View* view); 40 41 // The type of decoration. 42 DecorationType type; 43 44 // The y position of the view inside its parent. 45 int y; 46 47 // If 0, will use the preferred height of the view. 48 int height; 49 50 // Used for resizeable decorations, indicates the maximum fraction of the 51 // location bar that can be taken by this decoration, 0 for non-resizable 52 // decorations. If non-zero, |type| must not be AUTO_COLLAPSE. 53 double max_fraction; 54 55 // Padding to use if the decoration is the first element next to the edge. 56 int edge_item_padding; 57 58 // Padding to use if the decoration follows another decoration. 59 int item_padding; 60 61 // Padding built into the decoration and that should be removed, on 62 // both sides, during layout. 63 int builtin_padding; 64 65 views::View* view; 66 67 // The width computed by the layout process. 68 double computed_width; 69}; 70 71LocationBarDecoration::LocationBarDecoration(DecorationType type, 72 int y, 73 int height, 74 double max_fraction, 75 int edge_item_padding, 76 int item_padding, 77 int builtin_padding, 78 views::View* view) 79 : type(type), 80 y(y), 81 height(height), 82 max_fraction(max_fraction), 83 edge_item_padding(edge_item_padding), 84 item_padding(item_padding), 85 builtin_padding(builtin_padding), 86 view(view), 87 computed_width(0) { 88 if (type == NORMAL) { 89 DCHECK_GE(max_fraction, 0.0); 90 } else { 91 DCHECK_EQ(0.0, max_fraction); 92 } 93} 94 95 96// LocationBarLayout --------------------------------------------------------- 97 98LocationBarLayout::LocationBarLayout(Position position, 99 int item_edit_padding, 100 int edge_edit_padding) 101 : position_(position), 102 item_edit_padding_(item_edit_padding), 103 edge_edit_padding_(edge_edit_padding) {} 104 105 106LocationBarLayout::~LocationBarLayout() { 107} 108 109void LocationBarLayout::AddDecoration(int y, 110 int height, 111 bool auto_collapse, 112 double max_fraction, 113 int edge_item_padding, 114 int item_padding, 115 int builtin_padding, 116 views::View* view) { 117 decorations_.push_back(new LocationBarDecoration( 118 auto_collapse ? AUTO_COLLAPSE : NORMAL, y, height, max_fraction, 119 edge_item_padding, item_padding, builtin_padding, view)); 120} 121 122void LocationBarLayout::AddDecoration(int y, 123 int height, 124 int builtin_padding, 125 views::View* view) { 126 decorations_.push_back(new LocationBarDecoration( 127 NORMAL, y, height, 0, LocationBarView::GetEdgeItemPadding(), 128 LocationBarView::GetItemPadding(), builtin_padding, view)); 129} 130 131void LocationBarLayout::AddSeparator(int y, 132 int height, 133 int padding_from_previous_item, 134 views::View* separator) { 135 // Edge item padding won't apply since a separator won't be by the edge, so 136 // use 0 for more accurate evaluation of |entry_width| in LayoutPass1(). 137 decorations_.push_back(new LocationBarDecoration( 138 SEPARATOR, y, height, 0, 0, padding_from_previous_item, 0, separator)); 139} 140 141void LocationBarLayout::LayoutPass1(int* entry_width) { 142 bool first_item = true; 143 bool at_least_one_visible = false; 144 for (Decorations::iterator it(decorations_.begin()); it != decorations_.end(); 145 ++it) { 146 // Autocollapsing decorations are ignored in this pass. 147 if ((*it)->type != AUTO_COLLAPSE) { 148 at_least_one_visible = true; 149 *entry_width -= -2 * (*it)->builtin_padding + 150 (first_item ? (*it)->edge_item_padding : (*it)->item_padding); 151 } 152 first_item = false; 153 // Resizing decorations are ignored in this pass. 154 if (((*it)->type != AUTO_COLLAPSE) && ((*it)->max_fraction == 0.0)) { 155 (*it)->computed_width = (*it)->view->GetPreferredSize().width(); 156 *entry_width -= (*it)->computed_width; 157 } 158 } 159 *entry_width -= at_least_one_visible ? item_edit_padding_ : 160 edge_edit_padding_; 161} 162 163void LocationBarLayout::LayoutPass2(int *entry_width) { 164 for (Decorations::iterator it(decorations_.begin()); it != decorations_.end(); 165 ++it) { 166 if ((*it)->max_fraction > 0.0) { 167 int max_width = static_cast<int>(*entry_width * (*it)->max_fraction); 168 (*it)->computed_width = std::min((*it)->view->GetPreferredSize().width(), 169 std::max((*it)->view->GetMinimumSize().width(), max_width)); 170 *entry_width -= (*it)->computed_width; 171 } 172 } 173} 174 175void LocationBarLayout::LayoutPass3(gfx::Rect* bounds, int* available_width) { 176 SetVisibilityForDecorations(available_width); 177 HideUnneededSeparators(available_width); 178 SetBoundsForDecorations(bounds); 179} 180 181void LocationBarLayout::SetVisibilityForDecorations(int* available_width) { 182 bool first_visible = true; 183 for (Decorations::iterator it(decorations_.begin()); it != decorations_.end(); 184 ++it) { 185 // Collapse decorations if needed. 186 if ((*it)->type == AUTO_COLLAPSE) { 187 int padding = -2 * (*it)->builtin_padding + 188 (first_visible ? (*it)->edge_item_padding : (*it)->item_padding); 189 // Try preferred size, if it fails try minimum size, if it fails 190 // collapse. 191 (*it)->computed_width = (*it)->view->GetPreferredSize().width(); 192 if ((*it)->computed_width + padding > *available_width) 193 (*it)->computed_width = (*it)->view->GetMinimumSize().width(); 194 if ((*it)->computed_width + padding > *available_width) { 195 (*it)->computed_width = 0; 196 (*it)->view->SetVisible(false); 197 } else { 198 (*it)->view->SetVisible(true); 199 (*available_width) -= (*it)->computed_width + padding; 200 } 201 } else { 202 (*it)->view->SetVisible(true); 203 } 204 205 if ((*it)->view->visible()) 206 first_visible = false; 207 } 208} 209 210void LocationBarLayout::HideUnneededSeparators(int* available_width) { 211 // Initialize |trailing_separator| to first decoration so that any leading 212 // separator will be hidden. 213 Decorations::iterator trailing_separator = decorations_.begin(); 214 for (Decorations::iterator it(decorations_.begin()); it != decorations_.end(); 215 ++it) { 216 if ((*it)->type == SEPARATOR) { 217 if (trailing_separator != decorations_.end()) { 218 (*it)->view->SetVisible(false); 219 // Add back what was subtracted when setting this separator visible in 220 // LayoutPass1(). 221 (*available_width) += (*it)->item_padding + (*it)->computed_width; 222 } else { 223 trailing_separator = it; 224 } 225 } else if ((*it)->view->visible()) { 226 trailing_separator = decorations_.end(); 227 } 228 } 229 // If there's a trailing separator, hide it. 230 if (trailing_separator != decorations_.end()) { 231 (*trailing_separator)->view->SetVisible(false); 232 // Add back what was subtracted when setting this separator visible in 233 // LayoutPass1(). 234 (*available_width) += (*trailing_separator)->item_padding + 235 (*trailing_separator)->computed_width; 236 } 237} 238 239void LocationBarLayout::SetBoundsForDecorations(gfx::Rect* bounds) { 240 bool first_visible = true; 241 for (Decorations::iterator it(decorations_.begin()); it != decorations_.end(); 242 ++it) { 243 LocationBarDecoration* curr = *it; 244 if (!curr->view->visible()) 245 continue; 246 int padding = -curr->builtin_padding + 247 (first_visible ? curr->edge_item_padding : curr->item_padding); 248 first_visible = false; 249 int x = (position_ == LEFT_EDGE) ? (bounds->x() + padding) : 250 (bounds->right() - padding - curr->computed_width); 251 int height = curr->height == 0 ? 252 curr->view->GetPreferredSize().height() : curr->height; 253 curr->view->SetBounds(x, curr->y, curr->computed_width, height); 254 bounds->set_width(bounds->width() - padding - curr->computed_width + 255 curr->builtin_padding); 256 if (position_ == LEFT_EDGE) { 257 bounds->set_x( 258 bounds->x() + padding + curr->computed_width - curr->builtin_padding); 259 } 260 } 261 int final_padding = first_visible ? edge_edit_padding_ : item_edit_padding_; 262 bounds->set_width(bounds->width() - final_padding); 263 if (position_ == LEFT_EDGE) 264 bounds->set_x(bounds->x() + final_padding); 265} 266