OLD | NEW |
(Empty) | |
| 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/tabs/touch_tab_strip_layout.h" |
| 6 |
| 7 #include <stdio.h> |
| 8 |
| 9 #include "base/logging.h" |
| 10 #include "base/string_number_conversions.h" |
| 11 |
| 12 TouchTabStripLayout::TouchTabStripLayout(const gfx::Size& size, |
| 13 int padding, |
| 14 int stacked_padding, |
| 15 int max_stacked_count, |
| 16 views::ViewModel* view_model) |
| 17 : size_(size), |
| 18 padding_(padding), |
| 19 stacked_padding_(stacked_padding), |
| 20 max_stacked_count_(max_stacked_count), |
| 21 view_model_(view_model), |
| 22 x_(0), |
| 23 width_(0), |
| 24 mini_tab_count_(0), |
| 25 active_index_(-1) { |
| 26 } |
| 27 |
| 28 TouchTabStripLayout::~TouchTabStripLayout() { |
| 29 } |
| 30 |
| 31 void TouchTabStripLayout::SetXAndMiniCount(int x, int mini_tab_count) { |
| 32 x_ = x; |
| 33 mini_tab_count_ = mini_tab_count; |
| 34 SetIdealBoundsAt(active_index(), ConstrainActiveX(ideal_x(active_index()))); |
| 35 LayoutByTabOffsetAfter(active_index()); |
| 36 LayoutByTabOffsetBefore(active_index()); |
| 37 } |
| 38 |
| 39 void TouchTabStripLayout::SetWidth(int width) { |
| 40 if (width_ == width) |
| 41 return; |
| 42 |
| 43 width_ = width; |
| 44 if (!requires_stacking()) { |
| 45 ResetToIdealState(); |
| 46 return; |
| 47 } |
| 48 SetActiveBoundsAndLayoutFromActiveTab(); |
| 49 } |
| 50 |
| 51 void TouchTabStripLayout::SetActiveIndex(int index) { |
| 52 int old = active_index(); |
| 53 active_index_ = index; |
| 54 if (old == active_index() || !requires_stacking()) |
| 55 return; |
| 56 SetIdealBoundsAt(active_index(), ConstrainActiveX(ideal_x(active_index()))); |
| 57 LayoutByTabOffsetBefore(active_index()); |
| 58 LayoutByTabOffsetAfter(active_index()); |
| 59 AdjustStackedTabs(); |
| 60 } |
| 61 |
| 62 void TouchTabStripLayout::DragActiveTab(int delta) { |
| 63 if (delta == 0 && !requires_stacking()) |
| 64 return; |
| 65 int initial_x = ideal_x(active_index()); |
| 66 // If we're at a particular edge and start dragging, reset to ideal state. |
| 67 if ((delta > 0 && initial_x == GetMinX(active_index())) || |
| 68 (delta < 0 && initial_x == GetMaxX(active_index()))) { |
| 69 ResetToIdealState(); |
| 70 } |
| 71 int x = delta > 0 ? |
| 72 std::min(initial_x + delta, GetMaxX(active_index())) : |
| 73 std::max(initial_x + delta, GetMinX(active_index())); |
| 74 if (x != initial_x) { |
| 75 SetIdealBoundsAt(active_index(), x); |
| 76 LayoutByTabOffsetAfter(active_index()); |
| 77 LayoutByTabOffsetBefore(active_index()); |
| 78 delta -= (x - initial_x); |
| 79 } |
| 80 if (delta > 0) |
| 81 ExpandTabsBefore(active_index(), delta); |
| 82 else if (delta < 0) |
| 83 ExpandTabsAfter(active_index(), -delta); |
| 84 AdjustStackedTabs(); |
| 85 } |
| 86 |
| 87 void TouchTabStripLayout::AddTab(int index, |
| 88 int add_types, |
| 89 int start_x) { |
| 90 if (add_types & kAddTypeActive) |
| 91 active_index_ = index; |
| 92 else if (active_index_ >= index) |
| 93 active_index_++; |
| 94 if (add_types & kAddTypeMini) |
| 95 mini_tab_count_++; |
| 96 x_ = start_x; |
| 97 if (!requires_stacking() || normal_tab_count() <= 1) { |
| 98 ResetToIdealState(); |
| 99 return; |
| 100 } |
| 101 int active_x = (index + 1 == tab_count()) ? |
| 102 width_ - size_.width() : ideal_x(index + 1); |
| 103 SetIdealBoundsAt(active_index(), ConstrainActiveX(active_x)); |
| 104 LayoutByTabOffsetAfter(active_index()); |
| 105 LayoutByTabOffsetBefore(active_index()); |
| 106 AdjustStackedTabs(); |
| 107 } |
| 108 |
| 109 void TouchTabStripLayout::RemoveTab(int index, int start_x, int old_x) { |
| 110 if (index == active_index_) |
| 111 active_index_ = std::min(active_index_, tab_count() - 1); |
| 112 else if (index > active_index_) |
| 113 active_index_--; |
| 114 bool removed_mini_tab = index < mini_tab_count_; |
| 115 if (removed_mini_tab) { |
| 116 mini_tab_count_--; |
| 117 DCHECK_GE(mini_tab_count_, 0); |
| 118 } |
| 119 int delta = start_x - x_; |
| 120 x_ = start_x; |
| 121 if (!requires_stacking()) { |
| 122 ResetToIdealState(); |
| 123 return; |
| 124 } |
| 125 if (removed_mini_tab) { |
| 126 for (int i = mini_tab_count_; i < tab_count(); ++i) |
| 127 SetIdealBoundsAt(i, ideal_x(i) + delta); |
| 128 } |
| 129 SetActiveBoundsAndLayoutFromActiveTab(); |
| 130 AdjustStackedTabs(); |
| 131 } |
| 132 |
| 133 void TouchTabStripLayout::MoveTab(int from, |
| 134 int to, |
| 135 int new_active_index, |
| 136 int start_x, |
| 137 int mini_tab_count) { |
| 138 x_ = start_x; |
| 139 mini_tab_count_ = mini_tab_count; |
| 140 active_index_ = new_active_index; |
| 141 SetIdealBoundsAt(active_index(), ConstrainActiveX(ideal_x(active_index()))); |
| 142 LayoutByTabOffsetAfter(active_index()); |
| 143 LayoutByTabOffsetBefore(active_index()); |
| 144 AdjustStackedTabs(); |
| 145 } |
| 146 |
| 147 void TouchTabStripLayout::Reset(int x, |
| 148 int width, |
| 149 int mini_tab_count, |
| 150 int active_index) { |
| 151 x_ = x; |
| 152 width_ = width; |
| 153 mini_tab_count_ = mini_tab_count; |
| 154 active_index_ = active_index; |
| 155 ResetToIdealState(); |
| 156 } |
| 157 |
| 158 void TouchTabStripLayout::ResetToIdealState() { |
| 159 if (tab_count() == mini_tab_count_) |
| 160 return; |
| 161 |
| 162 if (!requires_stacking()) { |
| 163 SetIdealBoundsAt(mini_tab_count_, x_); |
| 164 LayoutByTabOffsetAfter(mini_tab_count_); |
| 165 return; |
| 166 } |
| 167 |
| 168 if (normal_tab_count() == 1) { |
| 169 // TODO: might want to shrink the tab here. |
| 170 SetIdealBoundsAt(mini_tab_count_, 0); |
| 171 return; |
| 172 } |
| 173 |
| 174 int available_width = width_ - x_; |
| 175 int leading_count = active_index() - mini_tab_count_; |
| 176 int trailing_count = tab_count() - active_index(); |
| 177 if (width_for_count(leading_count + 1) + max_stacked_width() < |
| 178 available_width) { |
| 179 SetIdealBoundsAt(mini_tab_count_, x_); |
| 180 LayoutByTabOffsetAfter(mini_tab_count_); |
| 181 } else if (width_for_count(trailing_count) + max_stacked_width() < |
| 182 available_width) { |
| 183 SetIdealBoundsAt(tab_count() - 1, width_ - size_.width()); |
| 184 LayoutByTabOffsetBefore(tab_count() - 1); |
| 185 } else { |
| 186 int index = active_index(); |
| 187 do { |
| 188 int stacked_padding = stacked_padding_for_count(index - mini_tab_count_); |
| 189 SetIdealBoundsAt(index, x_ + stacked_padding); |
| 190 LayoutByTabOffsetAfter(index); |
| 191 LayoutByTabOffsetBefore(index); |
| 192 index--; |
| 193 } while (index >= mini_tab_count_ && ideal_x(mini_tab_count_) != x_ && |
| 194 ideal_x(tab_count() - 1) != width_ - size_.width()); |
| 195 } |
| 196 AdjustStackedTabs(); |
| 197 } |
| 198 |
| 199 int TouchTabStripLayout::ConstrainActiveX(int x) const { |
| 200 return std::min(GetMaxX(active_index()), |
| 201 std::max(GetMinX(active_index()), x)); |
| 202 } |
| 203 |
| 204 void TouchTabStripLayout::SetActiveBoundsAndLayoutFromActiveTab() { |
| 205 int x = ConstrainActiveX(ideal_x(active_index())); |
| 206 SetIdealBoundsAt(active_index(), x); |
| 207 LayoutUsingCurrentBefore(active_index()); |
| 208 LayoutUsingCurrentAfter(active_index()); |
| 209 AdjustStackedTabs(); |
| 210 } |
| 211 |
| 212 void TouchTabStripLayout::LayoutByTabOffsetAfter(int index) { |
| 213 for (int i = index + 1; i < tab_count(); ++i) { |
| 214 int max_x = width_ - size_.width() - |
| 215 stacked_padding_for_count(tab_count() - i - 1); |
| 216 int x = std::min(max_x, |
| 217 view_model_->ideal_bounds(i - 1).x() + tab_offset()); |
| 218 SetIdealBoundsAt(i, x); |
| 219 } |
| 220 } |
| 221 |
| 222 void TouchTabStripLayout::LayoutByTabOffsetBefore(int index) { |
| 223 for (int i = index - 1; i >= mini_tab_count_; --i) { |
| 224 int min_x = x_ + stacked_padding_for_count(i - mini_tab_count_); |
| 225 int x = std::max(min_x, ideal_x(i + 1) - (tab_offset())); |
| 226 SetIdealBoundsAt(i, x); |
| 227 } |
| 228 } |
| 229 |
| 230 void TouchTabStripLayout::LayoutUsingCurrentAfter(int index) { |
| 231 for (int i = index + 1; i < tab_count(); ++i) { |
| 232 int min_x = width_ - width_for_count(tab_count() - i); |
| 233 int x = std::max(min_x, |
| 234 std::min(ideal_x(i), ideal_x(i - 1) + tab_offset())); |
| 235 x = std::min(GetMaxX(i), x); |
| 236 SetIdealBoundsAt(i, x); |
| 237 } |
| 238 } |
| 239 |
| 240 void TouchTabStripLayout::LayoutUsingCurrentBefore(int index) { |
| 241 for (int i = index - 1; i >= mini_tab_count_; --i) { |
| 242 int max_x = x_ + width_for_count(i - mini_tab_count_); |
| 243 SetIdealBoundsAt( |
| 244 i, std::min(max_x, |
| 245 std::max(ideal_x(i), ideal_x(i + 1) - tab_offset()))); |
| 246 } |
| 247 } |
| 248 |
| 249 void TouchTabStripLayout::ExpandTabsBefore(int index, int delta) { |
| 250 if (index == mini_tab_count_ + 1) |
| 251 return; // Nothing to expand. |
| 252 |
| 253 for (int i = index - 1; i > mini_tab_count_ && delta > 0; --i) { |
| 254 int to_resize = std::min(delta, GetMaxXCompressed(i) - ideal_x(i)); |
| 255 if (to_resize <= 0) |
| 256 continue; |
| 257 SetIdealBoundsAt(i, ideal_x(i) + to_resize); |
| 258 delta -= to_resize; |
| 259 LayoutByTabOffsetBefore(i); |
| 260 } |
| 261 } |
| 262 |
| 263 void TouchTabStripLayout::ExpandTabsAfter(int index, int delta) { |
| 264 if (index == tab_count() - 1) |
| 265 return; // Nothing to expand. |
| 266 |
| 267 for (int i = index + 1; i < tab_count() - 1 && delta > 0; ++i) { |
| 268 int to_resize = std::min(ideal_x(i) - GetMinXCompressed(i), delta); |
| 269 if (to_resize <= 0) |
| 270 continue; |
| 271 SetIdealBoundsAt(i, ideal_x(i) - to_resize); |
| 272 delta -= to_resize; |
| 273 LayoutByTabOffsetAfter(i); |
| 274 } |
| 275 } |
| 276 |
| 277 void TouchTabStripLayout::AdjustStackedTabs() { |
| 278 if (!requires_stacking() || mini_tab_count_ == tab_count()) |
| 279 return; |
| 280 |
| 281 AdjustLeadingStackedTabs(); |
| 282 AdjustTrailingStackedTabs(); |
| 283 } |
| 284 |
| 285 void TouchTabStripLayout::AdjustLeadingStackedTabs() { |
| 286 int index = mini_tab_count_ + 1; |
| 287 while (index < active_index() && |
| 288 ideal_x(index) - ideal_x(index - 1) <= stacked_padding_ && |
| 289 ideal_x(index) <= x_ + max_stacked_width()) { |
| 290 index++; |
| 291 } |
| 292 if (ideal_x(index) - ideal_x(index - 1) <= stacked_padding_ && |
| 293 ideal_x(index) <= x_ + max_stacked_width()) { |
| 294 index++; |
| 295 } |
| 296 if (index <= mini_tab_count_ + max_stacked_count_ - 1) |
| 297 return; |
| 298 int max_stacked = index; |
| 299 int x = x_; |
| 300 index = mini_tab_count_; |
| 301 for (; index < max_stacked - max_stacked_count_ - 1; ++index) |
| 302 SetIdealBoundsAt(index, x); |
| 303 for (; index < max_stacked; ++index, x += stacked_padding_) |
| 304 SetIdealBoundsAt(index, x); |
| 305 } |
| 306 |
| 307 void TouchTabStripLayout::AdjustTrailingStackedTabs() { |
| 308 int index = tab_count() - 1; |
| 309 int max_stacked_x = width_ - size_.width() - max_stacked_width(); |
| 310 while (index > active_index() && |
| 311 ideal_x(index) - ideal_x(index - 1) <= stacked_padding_ && |
| 312 ideal_x(index - 1) >= max_stacked_x) { |
| 313 index--; |
| 314 } |
| 315 if (ideal_x(index) - ideal_x(index - 1) <= stacked_padding_ && |
| 316 ideal_x(index - 1) >= max_stacked_x) { |
| 317 index--; |
| 318 } |
| 319 if (index >= tab_count() - max_stacked_count_) |
| 320 return; |
| 321 int first_stacked = index; |
| 322 int x = width_ - size_.width() - |
| 323 std::min(tab_count() - first_stacked, max_stacked_count_) * |
| 324 stacked_padding_; |
| 325 for (; index < first_stacked + max_stacked_count_; |
| 326 ++index, x += stacked_padding_) { |
| 327 SetIdealBoundsAt(index, x); |
| 328 } |
| 329 for (; index < tab_count(); ++index) |
| 330 SetIdealBoundsAt(index, x); |
| 331 } |
| 332 |
| 333 void TouchTabStripLayout::SetIdealBoundsAt(int index, int x) { |
| 334 view_model_->set_ideal_bounds(index, gfx::Rect(gfx::Point(x, 0), size_)); |
| 335 } |
| 336 |
| 337 int TouchTabStripLayout::GetMinX(int index) const { |
| 338 int leading_count = index - mini_tab_count_; |
| 339 int trailing_count = tab_count() - index; |
| 340 return std::max(x_ + stacked_padding_for_count(leading_count), |
| 341 width_ - width_for_count(trailing_count)); |
| 342 } |
| 343 |
| 344 int TouchTabStripLayout::GetMaxX(int index) const { |
| 345 int leading_count = index - mini_tab_count_; |
| 346 int trailing_count = tab_count() - index - 1; |
| 347 int trailing_offset = stacked_padding_for_count(trailing_count); |
| 348 int leading_size = width_for_count(leading_count) + x_; |
| 349 if (leading_count > 0) |
| 350 leading_size += padding_; |
| 351 return std::min(width_ - trailing_offset - size_.width(), leading_size); |
| 352 } |
| 353 |
| 354 int TouchTabStripLayout::GetMaxXCompressed(int index) const { |
| 355 DCHECK_LT(index, active_index()); |
| 356 DCHECK_GT(index, mini_tab_count_); |
| 357 int trailing = active_index() - index; |
| 358 return std::min( |
| 359 x_ + width_for_count(index - mini_tab_count_) + padding_, |
| 360 ideal_x(active_index()) - stacked_padding_for_count(trailing)); |
| 361 } |
| 362 |
| 363 int TouchTabStripLayout::GetMinXCompressed(int index) const { |
| 364 DCHECK_GT(index, active_index()); |
| 365 return std::max( |
| 366 width_ - width_for_count(tab_count() - index), |
| 367 ideal_x(active_index()) + |
| 368 stacked_padding_for_count(index - active_index())); |
| 369 |
| 370 } |
| 371 |
| 372 #if !defined(NDEBUG) |
| 373 std::string TouchTabStripLayout::BoundsString() const { |
| 374 std::string result; |
| 375 for (int i = 0; i < view_model_->view_size(); ++i) { |
| 376 if (!result.empty()) |
| 377 result += " "; |
| 378 if (i == active_index()) |
| 379 result += "["; |
| 380 result += base::IntToString(view_model_->ideal_bounds(i).x()); |
| 381 if (i == active_index()) |
| 382 result += "]"; |
| 383 } |
| 384 return result; |
| 385 } |
| 386 #endif |
OLD | NEW |