Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(41)

Side by Side Diff: chrome/browser/ui/views/tabs/tab_strip.cc

Issue 10213011: Attempt 3 at a better touch tabstrip. There is still a bunch to do, (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Remove Tab::GetTouchModeMinimumSize Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ui/views/tabs/tab_strip.h" 5 #include "chrome/browser/ui/views/tabs/tab_strip.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <iterator> 8 #include <iterator>
9 #include <vector> 9 #include <vector>
10 10
11 #if defined(OS_WIN) 11 #if defined(OS_WIN)
12 #include <windowsx.h> 12 #include <windowsx.h>
13 #endif 13 #endif
14 14
15 #include "base/command_line.h" 15 #include "base/command_line.h"
16 #include "base/compiler_specific.h" 16 #include "base/compiler_specific.h"
17 #include "base/stl_util.h" 17 #include "base/stl_util.h"
18 #include "base/utf_string_conversions.h" 18 #include "base/utf_string_conversions.h"
19 #include "chrome/browser/defaults.h" 19 #include "chrome/browser/defaults.h"
20 #include "chrome/browser/tabs/tab_strip_selection_model.h" 20 #include "chrome/browser/tabs/tab_strip_selection_model.h"
21 #include "chrome/browser/themes/theme_service.h" 21 #include "chrome/browser/themes/theme_service.h"
22 #include "chrome/browser/ui/view_ids.h" 22 #include "chrome/browser/ui/view_ids.h"
23 #include "chrome/browser/ui/views/tabs/tab.h" 23 #include "chrome/browser/ui/views/tabs/tab.h"
24 #include "chrome/browser/ui/views/tabs/tab_drag_controller.h" 24 #include "chrome/browser/ui/views/tabs/tab_drag_controller.h"
25 #include "chrome/browser/ui/views/tabs/tab_strip_controller.h" 25 #include "chrome/browser/ui/views/tabs/tab_strip_controller.h"
26 #include "chrome/browser/ui/views/tabs/touch_tab_strip_layout.h"
26 #include "grit/generated_resources.h" 27 #include "grit/generated_resources.h"
27 #include "grit/theme_resources.h" 28 #include "grit/theme_resources.h"
28 #include "grit/theme_resources_standard.h" 29 #include "grit/theme_resources_standard.h"
29 #include "ui/base/accessibility/accessible_view_state.h" 30 #include "ui/base/accessibility/accessible_view_state.h"
30 #include "ui/base/animation/animation_container.h" 31 #include "ui/base/animation/animation_container.h"
31 #include "ui/base/animation/throb_animation.h" 32 #include "ui/base/animation/throb_animation.h"
32 #include "ui/base/dragdrop/drag_drop_types.h" 33 #include "ui/base/dragdrop/drag_drop_types.h"
33 #include "ui/base/l10n/l10n_util.h" 34 #include "ui/base/l10n/l10n_util.h"
34 #include "ui/base/resource/resource_bundle.h" 35 #include "ui/base/resource/resource_bundle.h"
35 #include "ui/base/touch/touch_mode_support.h" 36 #include "ui/base/touch/touch_mode_support.h"
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
82 83
83 // Size of the drop indicator. 84 // Size of the drop indicator.
84 static int drop_indicator_width; 85 static int drop_indicator_width;
85 static int drop_indicator_height; 86 static int drop_indicator_height;
86 87
87 static inline int Round(double x) { 88 static inline int Round(double x) {
88 // Why oh why is this not in a standard header? 89 // Why oh why is this not in a standard header?
89 return static_cast<int>(floor(x + 0.5)); 90 return static_cast<int>(floor(x + 0.5));
90 } 91 }
91 92
92 // Max width reserved for stacked tabs along a particular edge. 93 // Max number of stacked tabs.
93 static const double kMaxEdgeStackWidth = 24; 94 static const int kMaxStackedCount = 4;
95
96 // Padding between stacked tabs.
97 static const int kStackedPadding = 6;
94 98
95 namespace { 99 namespace {
96 100
97 // Animation delegate used when a dragged tab is released. When done sets the 101 // Animation delegate used when a dragged tab is released. When done sets the
98 // dragging state to false. 102 // dragging state to false.
99 class ResetDraggingStateDelegate 103 class ResetDraggingStateDelegate
100 : public views::BoundsAnimator::OwnedAnimationDelegate { 104 : public views::BoundsAnimator::OwnedAnimationDelegate {
101 public: 105 public:
102 explicit ResetDraggingStateDelegate(BaseTab* tab) : tab_(tab) { 106 explicit ResetDraggingStateDelegate(BaseTab* tab) : tab_(tab) {
103 } 107 }
(...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after
380 384
381 TabStrip::TabStrip(TabStripController* controller) 385 TabStrip::TabStrip(TabStripController* controller)
382 : controller_(controller), 386 : controller_(controller),
383 newtab_button_(NULL), 387 newtab_button_(NULL),
384 current_unselected_width_(Tab::GetStandardSize().width()), 388 current_unselected_width_(Tab::GetStandardSize().width()),
385 current_selected_width_(Tab::GetStandardSize().width()), 389 current_selected_width_(Tab::GetStandardSize().width()),
386 available_width_for_tabs_(-1), 390 available_width_for_tabs_(-1),
387 in_tab_close_(false), 391 in_tab_close_(false),
388 animation_container_(new ui::AnimationContainer()), 392 animation_container_(new ui::AnimationContainer()),
389 attaching_dragged_tab_(false), 393 attaching_dragged_tab_(false),
390 ALLOW_THIS_IN_INITIALIZER_LIST(bounds_animator_(this)), 394 ALLOW_THIS_IN_INITIALIZER_LIST(bounds_animator_(this)) {
391 first_visible_tab_index_(0),
392 num_visible_tabs_(0),
393 stacking_(TouchModeSupport::IsTouchOptimized()) {
394 Init(); 395 Init();
395 } 396 }
396 397
397 TabStrip::~TabStrip() { 398 TabStrip::~TabStrip() {
398 // The animations may reference the tabs. Shut down the animation before we 399 // The animations may reference the tabs. Shut down the animation before we
399 // delete the tabs. 400 // delete the tabs.
400 StopAnimating(false); 401 StopAnimating(false);
401 402
402 DestroyDragController(); 403 DestroyDragController();
403 404
(...skipping 18 matching lines...) Expand all
422 423
423 void TabStrip::StartHighlight(int model_index) { 424 void TabStrip::StartHighlight(int model_index) {
424 tab_at(model_index)->StartPulse(); 425 tab_at(model_index)->StartPulse();
425 } 426 }
426 427
427 void TabStrip::StopAllHighlighting() { 428 void TabStrip::StopAllHighlighting() {
428 for (int i = 0; i < tab_count(); ++i) 429 for (int i = 0; i < tab_count(); ++i)
429 tab_at(i)->StopPulse(); 430 tab_at(i)->StopPulse();
430 } 431 }
431 432
432 void TabStrip::AddTabAt(int model_index, const TabRendererData& data) { 433 void TabStrip::AddTabAt(int model_index,
434 const TabRendererData& data,
435 bool is_active) {
433 BaseTab* tab = CreateTab(); 436 BaseTab* tab = CreateTab();
434 tab->SetData(data); 437 tab->SetData(data);
435 UpdateTabsClosingMap(model_index, 1); 438 UpdateTabsClosingMap(model_index, 1);
436 tabs_.Add(tab, model_index); 439 tabs_.Add(tab, model_index);
437 AddChildView(tab); 440 AddChildView(tab);
441
442 if (touch_layout_.get()) {
443 GenerateIdealBoundsForMiniTabs(NULL);
444 int add_types = 0;
445 if (data.mini)
446 add_types |= TouchTabStripLayout::kAddTypeMini;
447 if (is_active)
448 add_types |= TouchTabStripLayout::kAddTypeActive;
449 touch_layout_->AddTab(model_index, add_types, GetStartXForNormalTabs());
450 }
451
438 // Don't animate the first tab, it looks weird, and don't animate anything 452 // Don't animate the first tab, it looks weird, and don't animate anything
439 // if the containing window isn't visible yet. 453 // if the containing window isn't visible yet.
440 if (tab_count() > 1 && GetWidget() && GetWidget()->IsVisible()) 454 if (tab_count() > 1 && GetWidget() && GetWidget()->IsVisible())
441 StartInsertTabAnimation(model_index); 455 StartInsertTabAnimation(model_index);
442 else 456 else
443 DoLayout(); 457 DoLayout();
444 } 458 }
445 459
446 void TabStrip::MoveTab(int from_model_index, int to_model_index) { 460 void TabStrip::MoveTab(int from_model_index,
461 int to_model_index,
462 const TabRendererData& data) {
447 DCHECK_GT(tabs_.view_size(), 0); 463 DCHECK_GT(tabs_.view_size(), 0);
448 BaseTab* last_tab = tab_at(tab_count() - 1); 464 BaseTab* last_tab = tab_at(tab_count() - 1);
449 tabs_.Move(from_model_index, to_model_index); 465 tab_at(from_model_index)->SetData(data);
466 if (touch_layout_.get()) {
467 tabs_.Move(from_model_index, to_model_index);
468 int mini_tab_count = 0;
469 int start_x = GenerateIdealBoundsForMiniTabs(&mini_tab_count);
470 touch_layout_->MoveTab(
471 from_model_index, to_model_index, controller_->GetActiveIndex(),
472 start_x, mini_tab_count);
473 } else {
474 tabs_.Move(from_model_index, to_model_index);
475 }
450 StartMoveTabAnimation(); 476 StartMoveTabAnimation();
451 if (TabDragController::IsAttachedTo(this) && 477 if (TabDragController::IsAttachedTo(this) &&
452 (last_tab != tab_at(tab_count() - 1) || last_tab->dragging())) { 478 (last_tab != tab_at(tab_count() - 1) || last_tab->dragging())) {
453 newtab_button_->SetVisible(false); 479 newtab_button_->SetVisible(false);
454 } 480 }
455 } 481 }
456 482
457 void TabStrip::RemoveTabAt(int model_index) { 483 void TabStrip::RemoveTabAt(int model_index) {
458 if (stacking_) { 484 if (touch_layout_.get()) {
459 int count = tab_count() - 1; 485 BaseTab* tab = tab_at(model_index);
460 if (first_visible_tab_index_ + num_visible_tabs_ > count) 486 tab->set_closing(true);
461 first_visible_tab_index_ = std::max(0, first_visible_tab_index_ - 1); 487 int old_x = tabs_.ideal_bounds(model_index).x();
488 // We still need to paint the tab until we actually remove it. Put it in
489 // tabs_closing_map_ so we can find it.
490 RemoveTabFromViewModel(model_index);
491 touch_layout_->RemoveTab(model_index, GenerateIdealBoundsForMiniTabs(NULL),
492 old_x);
493 ScheduleRemoveTabAnimation(tab);
494 } else if (in_tab_close_ && model_index != GetModelCount()) {
495 StartMouseInitiatedRemoveTabAnimation(model_index);
496 } else {
497 StartRemoveTabAnimation(model_index);
462 } 498 }
463
464 if (in_tab_close_ && model_index != GetModelCount())
465 StartMouseInitiatedRemoveTabAnimation(model_index);
466 else
467 StartRemoveTabAnimation(model_index);
468 } 499 }
469 500
470 void TabStrip::SetTabData(int model_index, const TabRendererData& data) { 501 void TabStrip::SetTabData(int model_index, const TabRendererData& data) {
471 BaseTab* tab = tab_at(model_index); 502 BaseTab* tab = tab_at(model_index);
472 bool mini_state_changed = tab->data().mini != data.mini; 503 bool mini_state_changed = tab->data().mini != data.mini;
473 tab->SetData(data); 504 tab->SetData(data);
474 505
475 if (mini_state_changed) { 506 if (mini_state_changed) {
507 if (touch_layout_.get()) {
508 int mini_tab_count = 0;
509 int start_x = GenerateIdealBoundsForMiniTabs(&mini_tab_count);
510 touch_layout_->SetXAndMiniCount(start_x, mini_tab_count);
511 }
476 if (GetWidget() && GetWidget()->IsVisible()) 512 if (GetWidget() && GetWidget()->IsVisible())
477 StartMiniTabAnimation(); 513 StartMiniTabAnimation();
478 else 514 else
479 DoLayout(); 515 DoLayout();
480 } 516 }
481 } 517 }
482 518
483 void TabStrip::PrepareForCloseAt(int model_index) { 519 void TabStrip::PrepareForCloseAt(int model_index) {
484 if (!in_tab_close_ && IsAnimating()) { 520 if (!in_tab_close_ && IsAnimating()) {
485 // Cancel any current animations. We do this as remove uses the current 521 // Cancel any current animations. We do this as remove uses the current
(...skipping 16 matching lines...) Expand all
502 available_width_for_tabs_ -= kMiniToNonMiniGap; 538 available_width_for_tabs_ -= kMiniToNonMiniGap;
503 } 539 }
504 } 540 }
505 541
506 in_tab_close_ = true; 542 in_tab_close_ = true;
507 AddMessageLoopObserver(); 543 AddMessageLoopObserver();
508 } 544 }
509 545
510 void TabStrip::SetSelection(const TabStripSelectionModel& old_selection, 546 void TabStrip::SetSelection(const TabStripSelectionModel& old_selection,
511 const TabStripSelectionModel& new_selection) { 547 const TabStripSelectionModel& new_selection) {
512 // Make sure the active tab is visible. 548 if (touch_layout_.get()) {
513 EnsureModelIndexIsVisible(new_selection.active()); 549 touch_layout_->SetActiveIndex(new_selection.active());
514 550 SchedulePaint();
515 // We have "tiny tabs" if the tabs are so tiny that the unselected ones are 551 AnimateToIdealBounds();
516 // a different size to the selected ones.
517 bool tiny_tabs = current_unselected_width_ != current_selected_width_;
518 if (!IsAnimating() && (!in_tab_close_ || tiny_tabs)) {
519 DoLayout();
520 } else { 552 } else {
521 SchedulePaint(); 553 // We have "tiny tabs" if the tabs are so tiny that the unselected ones are
554 // a different size to the selected ones.
555 bool tiny_tabs = current_unselected_width_ != current_selected_width_;
556 if (!IsAnimating() && (!in_tab_close_ || tiny_tabs)) {
557 DoLayout();
558 } else {
559 SchedulePaint();
560 }
522 } 561 }
523 562
524 TabStripSelectionModel::SelectedIndices no_longer_selected; 563 TabStripSelectionModel::SelectedIndices no_longer_selected;
525 std::insert_iterator<TabStripSelectionModel::SelectedIndices> 564 std::insert_iterator<TabStripSelectionModel::SelectedIndices>
526 it(no_longer_selected, no_longer_selected.begin()); 565 it(no_longer_selected, no_longer_selected.begin());
527 std::set_difference(old_selection.selected_indices().begin(), 566 std::set_difference(old_selection.selected_indices().begin(),
528 old_selection.selected_indices().end(), 567 old_selection.selected_indices().end(),
529 new_selection.selected_indices().begin(), 568 new_selection.selected_indices().begin(),
530 new_selection.selected_indices().end(), 569 new_selection.selected_indices().end(),
531 it); 570 it);
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
569 608
570 bool TabStrip::IsActiveDropTarget() const { 609 bool TabStrip::IsActiveDropTarget() const {
571 for (int i = 0; i < tab_count(); ++i) { 610 for (int i = 0; i < tab_count(); ++i) {
572 BaseTab* tab = tab_at(i); 611 BaseTab* tab = tab_at(i);
573 if (tab->dragging()) 612 if (tab->dragging())
574 return true; 613 return true;
575 } 614 }
576 return false; 615 return false;
577 } 616 }
578 617
579 bool TabStrip::IsStacking() const {
580 return stacking_;
581 }
582
583 const TabStripSelectionModel& TabStrip::GetSelectionModel() { 618 const TabStripSelectionModel& TabStrip::GetSelectionModel() {
584 return controller_->GetSelectionModel(); 619 return controller_->GetSelectionModel();
585 } 620 }
586 621
622 bool TabStrip::SupportsMultipleSelection() {
623 // TODO: currently only allow single selection in touch layout mode.
624 return touch_layout_.get() == NULL;
625 }
626
587 void TabStrip::SelectTab(BaseTab* tab) { 627 void TabStrip::SelectTab(BaseTab* tab) {
588 int model_index = GetModelIndexOfBaseTab(tab); 628 int model_index = GetModelIndexOfBaseTab(tab);
589 if (IsValidModelIndex(model_index)) 629 if (IsValidModelIndex(model_index))
590 controller_->SelectTab(model_index); 630 controller_->SelectTab(model_index);
591 } 631 }
592 632
593 void TabStrip::ExtendSelectionTo(BaseTab* tab) { 633 void TabStrip::ExtendSelectionTo(BaseTab* tab) {
594 int model_index = GetModelIndexOfBaseTab(tab); 634 int model_index = GetModelIndexOfBaseTab(tab);
595 if (IsValidModelIndex(model_index)) 635 if (IsValidModelIndex(model_index))
596 controller_->ExtendSelectionTo(model_index); 636 controller_->ExtendSelectionTo(model_index);
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after
814 854
815 void TabStrip::PaintChildren(gfx::Canvas* canvas) { 855 void TabStrip::PaintChildren(gfx::Canvas* canvas) {
816 // The view order doesn't match the paint order (tabs_ contains the tab 856 // The view order doesn't match the paint order (tabs_ contains the tab
817 // ordering). Additionally we need to paint the tabs that are closing in 857 // ordering). Additionally we need to paint the tabs that are closing in
818 // |tabs_closing_map_|. 858 // |tabs_closing_map_|.
819 Tab* active_tab = NULL; 859 Tab* active_tab = NULL;
820 std::vector<Tab*> tabs_dragging; 860 std::vector<Tab*> tabs_dragging;
821 std::vector<Tab*> selected_tabs; 861 std::vector<Tab*> selected_tabs;
822 bool is_dragging = false; 862 bool is_dragging = false;
823 int active_tab_index = -1; 863 int active_tab_index = -1;
864 bool stacking = touch_layout_.get() != NULL;
824 865
825 PaintClosingTabs(canvas, tab_count()); 866 PaintClosingTabs(canvas, tab_count());
826 867
827 for (int i = tab_count() - 1; i >= 0; --i) { 868 for (int i = tab_count() - 1; i >= 0; --i) {
828 Tab* tab = tab_at(i); 869 Tab* tab = tab_at(i);
829 if (tab->dragging() && !stacking_) { 870 if (tab->dragging() && !stacking) {
830 is_dragging = true; 871 is_dragging = true;
831 if (tab->IsActive()) { 872 if (tab->IsActive()) {
832 active_tab = tab; 873 active_tab = tab;
833 active_tab_index = i; 874 active_tab_index = i;
834 } else { 875 } else {
835 tabs_dragging.push_back(tab); 876 tabs_dragging.push_back(tab);
836 } 877 }
837 } else if (!tab->IsActive()) { 878 } else if (!tab->IsActive()) {
838 if (!tab->IsSelected()) { 879 if (!tab->IsSelected()) {
839 if (!stacking_) 880 if (!stacking)
840 tab->Paint(canvas); 881 tab->Paint(canvas);
841 } else { 882 } else {
842 // TODO(scottmg): Multiple selection may be incorrect in touch mode. 883 // TODO(scottmg): Multiple selection may be incorrect in touch mode.
843 selected_tabs.push_back(tab); 884 selected_tabs.push_back(tab);
844 } 885 }
845 } else { 886 } else {
846 active_tab = tab; 887 active_tab = tab;
847 active_tab_index = i; 888 active_tab_index = i;
848 } 889 }
849 PaintClosingTabs(canvas, i); 890 PaintClosingTabs(canvas, i);
850 } 891 }
851 892
852 // Draw from the left and then the right if we're in touch mode. 893 // Draw from the left and then the right if we're in touch mode.
853 if (stacking_ && active_tab_index >= 0) { 894 if (stacking && active_tab_index >= 0) {
854 for (int i = 0; i < active_tab_index; ++i) { 895 for (int i = 0; i < active_tab_index; ++i) {
855 Tab* tab = tab_at(i); 896 Tab* tab = tab_at(i);
856 tab->Paint(canvas); 897 tab->Paint(canvas);
857 } 898 }
858 899
859 for (int i = tab_count() - 1; i > active_tab_index; --i) { 900 for (int i = tab_count() - 1; i > active_tab_index; --i) {
860 Tab* tab = tab_at(i); 901 Tab* tab = tab_at(i);
861 tab->Paint(canvas); 902 tab->Paint(canvas);
862 } 903 }
863 } 904 }
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
913 954
914 std::string TabStrip::GetClassName() const { 955 std::string TabStrip::GetClassName() const {
915 return kViewClassName; 956 return kViewClassName;
916 } 957 }
917 958
918 gfx::Size TabStrip::GetPreferredSize() { 959 gfx::Size TabStrip::GetPreferredSize() {
919 // Report the minimum width as the size required for a single selected tab 960 // Report the minimum width as the size required for a single selected tab
920 // plus the new tab button. Don't base it on the actual number of tabs because 961 // plus the new tab button. Don't base it on the actual number of tabs because
921 // it's undesirable to have the minimum window size change when a new tab is 962 // it's undesirable to have the minimum window size change when a new tab is
922 // opened. 963 // opened.
923 int needed_width = Tab::GetMinimumSelectedSize().width(); 964 int needed_width = Tab::GetMinimumSelectedSize().width() - kTabHOffset +
924 needed_width += kNewTabButtonHOffset - kTabHOffset; 965 new_tab_button_width();
925 needed_width += newtab_button_bounds_.width();
926 return gfx::Size(needed_width, Tab::GetMinimumUnselectedSize().height()); 966 return gfx::Size(needed_width, Tab::GetMinimumUnselectedSize().height());
927 } 967 }
928 968
929 void TabStrip::OnDragEntered(const DropTargetEvent& event) { 969 void TabStrip::OnDragEntered(const DropTargetEvent& event) {
930 // Force animations to stop, otherwise it makes the index calculation tricky. 970 // Force animations to stop, otherwise it makes the index calculation tricky.
931 StopAnimating(true); 971 StopAnimating(true);
932 972
933 UpdateDropIndex(event); 973 UpdateDropIndex(event);
934 } 974 }
935 975
(...skipping 24 matching lines...) Expand all
960 controller()->PerformDrop(drop_before, drop_index, url); 1000 controller()->PerformDrop(drop_before, drop_index, url);
961 1001
962 return GetDropEffect(event); 1002 return GetDropEffect(event);
963 } 1003 }
964 1004
965 void TabStrip::GetAccessibleState(ui::AccessibleViewState* state) { 1005 void TabStrip::GetAccessibleState(ui::AccessibleViewState* state) {
966 state->role = ui::AccessibilityTypes::ROLE_PAGETABLIST; 1006 state->role = ui::AccessibilityTypes::ROLE_PAGETABLIST;
967 } 1007 }
968 1008
969 views::View* TabStrip::GetEventHandlerForPoint(const gfx::Point& point) { 1009 views::View* TabStrip::GetEventHandlerForPoint(const gfx::Point& point) {
970 // Return any view that isn't a Tab or this TabStrip immediately. We don't 1010 if (!touch_layout_.get()) {
971 // want to interfere. 1011 // Return any view that isn't a Tab or this TabStrip immediately. We don't
972 views::View* v = View::GetEventHandlerForPoint(point); 1012 // want to interfere.
973 if (v && v != this && v->GetClassName() != Tab::kViewClassName) 1013 views::View* v = View::GetEventHandlerForPoint(point);
974 return v; 1014 if (v && v != this && v->GetClassName() != Tab::kViewClassName)
1015 return v;
975 1016
976 // The view order doesn't match the ordering we paint in. |tabs_| contains 1017 // The display order doesn't necessarily match the child list order, so we
977 // the overall order. 1018 // walk the display list hit-testing Tabs. Since the active tab always
978 for (int i = 0; i < tab_count(); ++i) { 1019 // renders on top of adjacent tabs, it needs to be hit-tested before any
979 Tab* next_tab = i < (tab_count() - 1) ? tab_at(i + 1) : NULL; 1020 // left-adjacent Tab, so we look ahead for it as we walk.
980 if (next_tab && next_tab->IsActive() && IsPointInTab(next_tab, point)) 1021 for (int i = 0; i < tab_count(); ++i) {
981 return next_tab; 1022 Tab* next_tab = i < (tab_count() - 1) ? tab_at(i + 1) : NULL;
982 Tab* tab = tab_at(i); 1023 if (next_tab && next_tab->IsActive() && IsPointInTab(next_tab, point))
983 if (IsPointInTab(tab, point)) 1024 return next_tab;
984 return tab; 1025 if (IsPointInTab(tab_at(i), point))
1026 return tab_at(i);
1027 }
1028 } else {
1029 int active_tab_index = touch_layout_->active_index();
1030 if (active_tab_index != -1) {
1031 Tab* tab = FindTabForEvent(point, active_tab_index, -1);
1032 if (!tab)
1033 tab = FindTabForEvent(point, active_tab_index + 1, 1);
1034 if (tab) {
1035 gfx::Point point_in_tab_coords(point);
1036 View::ConvertPointToView(this, tab, &point_in_tab_coords);
1037 return tab->GetEventHandlerForPoint(point_in_tab_coords);
1038 }
1039 }
985 } 1040 }
986
987 return this; 1041 return this;
988 } 1042 }
989 1043
990 int TabStrip::GetMiniTabCount() const { 1044 int TabStrip::GetMiniTabCount() const {
991 int mini_count = 0; 1045 int mini_count = 0;
992 for (int i = 0; i < tab_count(); ++i) { 1046 while (mini_count < tab_count() && tab_at(mini_count)->data().mini)
993 if (tab_at(i)->data().mini) 1047 mini_count++;
994 mini_count++;
995 else
996 return mini_count;
997 }
998 return mini_count; 1048 return mini_count;
999 } 1049 }
1000 1050
1001 /////////////////////////////////////////////////////////////////////////////// 1051 ///////////////////////////////////////////////////////////////////////////////
1002 // TabStrip, views::ButtonListener implementation: 1052 // TabStrip, views::ButtonListener implementation:
1003 1053
1004 void TabStrip::ButtonPressed(views::Button* sender, const views::Event& event) { 1054 void TabStrip::ButtonPressed(views::Button* sender, const views::Event& event) {
1005 if (sender == newtab_button_) 1055 if (sender == newtab_button_)
1006 controller()->CreateNewTab(); 1056 controller()->CreateNewTab();
1007 } 1057 }
1008 1058
1009 /////////////////////////////////////////////////////////////////////////////// 1059 ///////////////////////////////////////////////////////////////////////////////
1010 // TabStrip, protected: 1060 // TabStrip, protected:
1011 1061
1012 void TabStrip::ViewHierarchyChanged(bool is_add,
1013 views::View* parent,
1014 views::View* child) {
1015 if (is_add && child == this)
1016 InitTabStripButtons();
1017 }
1018
1019 // Overridden to support automation. See automation_proxy_uitest.cc. 1062 // Overridden to support automation. See automation_proxy_uitest.cc.
1020 const views::View* TabStrip::GetViewByID(int view_id) const { 1063 const views::View* TabStrip::GetViewByID(int view_id) const {
1021 if (tab_count() > 0) { 1064 if (tab_count() > 0) {
1022 if (view_id == VIEW_ID_TAB_LAST) { 1065 if (view_id == VIEW_ID_TAB_LAST) {
1023 return tab_at(tab_count() - 1); 1066 return tab_at(tab_count() - 1);
1024 } else if ((view_id >= VIEW_ID_TAB_0) && (view_id < VIEW_ID_TAB_LAST)) { 1067 } else if ((view_id >= VIEW_ID_TAB_0) && (view_id < VIEW_ID_TAB_LAST)) {
1025 int index = view_id - VIEW_ID_TAB_0; 1068 int index = view_id - VIEW_ID_TAB_0;
1026 if (index >= 0 && index < tab_count()) { 1069 if (index >= 0 && index < tab_count()) {
1027 return tab_at(index); 1070 return tab_at(index);
1028 } else { 1071 } else {
(...skipping 24 matching lines...) Expand all
1053 *unselected_width = current_unselected_width_; 1096 *unselected_width = current_unselected_width_;
1054 *selected_width = current_selected_width_; 1097 *selected_width = current_selected_width_;
1055 } 1098 }
1056 1099
1057 /////////////////////////////////////////////////////////////////////////////// 1100 ///////////////////////////////////////////////////////////////////////////////
1058 // TabStrip, private: 1101 // TabStrip, private:
1059 1102
1060 void TabStrip::Init() { 1103 void TabStrip::Init() {
1061 set_id(VIEW_ID_TAB_STRIP); 1104 set_id(VIEW_ID_TAB_STRIP);
1062 newtab_button_bounds_.SetRect(0, 0, kNewTabButtonWidth, kNewTabButtonHeight); 1105 newtab_button_bounds_.SetRect(0, 0, kNewTabButtonWidth, kNewTabButtonHeight);
1106 newtab_button_ = new NewTabButton(this, this);
1107 newtab_button_->SetTooltipText(
1108 l10n_util::GetStringUTF16(IDS_TOOLTIP_NEW_TAB));
1109 newtab_button_->SetAccessibleName(
1110 l10n_util::GetStringUTF16(IDS_ACCNAME_NEWTAB));
1111 AddChildView(newtab_button_);
1063 if (drop_indicator_width == 0) { 1112 if (drop_indicator_width == 0) {
1064 // Direction doesn't matter, both images are the same size. 1113 // Direction doesn't matter, both images are the same size.
1065 SkBitmap* drop_image = GetDropArrowImage(true); 1114 SkBitmap* drop_image = GetDropArrowImage(true);
1066 drop_indicator_width = drop_image->width(); 1115 drop_indicator_width = drop_image->width();
1067 drop_indicator_height = drop_image->height(); 1116 drop_indicator_height = drop_image->height();
1068 } 1117 }
1069 } 1118 if (TouchModeSupport::IsTouchOptimized()) {
1070 1119 touch_layout_.reset(new TouchTabStripLayout(
1071 void TabStrip::InitTabStripButtons() { 1120 Tab::GetStandardSize(),
1072 newtab_button_ = new NewTabButton(this, this); 1121 kTabHOffset,
1073 newtab_button_->SetTooltipText( 1122 kStackedPadding,
1074 l10n_util::GetStringUTF16(IDS_TOOLTIP_NEW_TAB)); 1123 kMaxStackedCount,
1075 newtab_button_->SetAccessibleName( 1124 &tabs_));
1076 l10n_util::GetStringUTF16(IDS_ACCNAME_NEWTAB)); 1125 }
1077 AddChildView(newtab_button_);
1078 } 1126 }
1079 1127
1080 BaseTab* TabStrip::CreateTab() { 1128 BaseTab* TabStrip::CreateTab() {
1081 Tab* tab = new Tab(this); 1129 Tab* tab = new Tab(this);
1082 tab->set_animation_container(animation_container_.get()); 1130 tab->set_animation_container(animation_container_.get());
1083 return tab; 1131 return tab;
1084 } 1132 }
1085 1133
1086 void TabStrip::StartInsertTabAnimation(int model_index) { 1134 void TabStrip::StartInsertTabAnimation(int model_index) {
1087 PrepareForAnimation(); 1135 PrepareForAnimation();
(...skipping 26 matching lines...) Expand all
1114 1162
1115 void TabStrip::StartRemoveTabAnimation(int model_index) { 1163 void TabStrip::StartRemoveTabAnimation(int model_index) {
1116 PrepareForAnimation(); 1164 PrepareForAnimation();
1117 1165
1118 // Mark the tab as closing. 1166 // Mark the tab as closing.
1119 BaseTab* tab = tab_at(model_index); 1167 BaseTab* tab = tab_at(model_index);
1120 tab->set_closing(true); 1168 tab->set_closing(true);
1121 1169
1122 RemoveTabFromViewModel(model_index); 1170 RemoveTabFromViewModel(model_index);
1123 1171
1172 ScheduleRemoveTabAnimation(tab);
1173 }
1174
1175 void TabStrip::ScheduleRemoveTabAnimation(BaseTab* tab) {
1124 // Start an animation for the tabs. 1176 // Start an animation for the tabs.
1125 GenerateIdealBounds(); 1177 GenerateIdealBounds();
1126 AnimateToIdealBounds(); 1178 AnimateToIdealBounds();
1127 1179
1128 // Animate the tab being closed to 0x0. 1180 // Animate the tab being closed to 0x0.
1129 gfx::Rect tab_bounds = tab->bounds(); 1181 gfx::Rect tab_bounds = tab->bounds();
1130 tab_bounds.set_width(0); 1182 tab_bounds.set_width(0);
1131 bounds_animator_.AnimateViewTo(tab, tab_bounds); 1183 bounds_animator_.AnimateViewTo(tab, tab_bounds);
1132 1184
1133 // Register delegate to do cleanup when done, BoundsAnimator takes 1185 // Register delegate to do cleanup when done, BoundsAnimator takes
(...skipping 16 matching lines...) Expand all
1150 1202
1151 bounds_animator_.Cancel(); 1203 bounds_animator_.Cancel();
1152 1204
1153 if (layout) 1205 if (layout)
1154 DoLayout(); 1206 DoLayout();
1155 } 1207 }
1156 1208
1157 void TabStrip::AnimateToIdealBounds() { 1209 void TabStrip::AnimateToIdealBounds() {
1158 for (int i = 0; i < tab_count(); ++i) { 1210 for (int i = 0; i < tab_count(); ++i) {
1159 Tab* tab = tab_at(i); 1211 Tab* tab = tab_at(i);
1160 if (!tab->closing() && !tab->dragging()) 1212 if (!tab->dragging())
1161 bounds_animator_.AnimateViewTo(tab, ideal_bounds(i)); 1213 bounds_animator_.AnimateViewTo(tab, ideal_bounds(i));
1162 } 1214 }
1163 1215
1164 bounds_animator_.AnimateViewTo(newtab_button_, newtab_button_bounds_); 1216 bounds_animator_.AnimateViewTo(newtab_button_, newtab_button_bounds_);
1165 } 1217 }
1166 1218
1167 bool TabStrip::ShouldHighlightCloseButtonAfterRemove() { 1219 bool TabStrip::ShouldHighlightCloseButtonAfterRemove() {
1168 return in_tab_close_; 1220 return in_tab_close_;
1169 } 1221 }
1170 1222
1171 void TabStrip::DoLayout() { 1223 void TabStrip::DoLayout() {
1172 last_layout_size_ = size(); 1224 last_layout_size_ = size();
1173 1225
1174 StopAnimating(false); 1226 StopAnimating(false);
1175 1227
1228 if (touch_layout_.get())
1229 touch_layout_->SetWidth(size().width() - new_tab_button_width());
1230
1176 GenerateIdealBounds(); 1231 GenerateIdealBounds();
1177 1232
1178 views::ViewModelUtils::SetViewBoundsToIdealBounds(tabs_); 1233 views::ViewModelUtils::SetViewBoundsToIdealBounds(tabs_);
1179 1234
1180 SchedulePaint(); 1235 SchedulePaint();
1181 1236
1182 // It is possible we don't have a new tab button yet. 1237 if (SizeTabButtonToTopOfTabStrip()) {
1183 if (newtab_button_) { 1238 newtab_button_bounds_.set_height(
1184 if (SizeTabButtonToTopOfTabStrip()) { 1239 kNewTabButtonHeight + kNewTabButtonVOffset);
1185 newtab_button_bounds_.set_height( 1240 newtab_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT,
1186 kNewTabButtonHeight + kNewTabButtonVOffset); 1241 views::ImageButton::ALIGN_BOTTOM);
1187 newtab_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT, 1242 } else {
1188 views::ImageButton::ALIGN_BOTTOM); 1243 newtab_button_bounds_.set_height(kNewTabButtonHeight);
1189 } else { 1244 newtab_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT,
1190 newtab_button_bounds_.set_height(kNewTabButtonHeight); 1245 views::ImageButton::ALIGN_TOP);
1191 newtab_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT,
1192 views::ImageButton::ALIGN_TOP);
1193 }
1194 newtab_button_->SetBoundsRect(newtab_button_bounds_);
1195 } 1246 }
1247 newtab_button_->SetBoundsRect(newtab_button_bounds_);
1196 } 1248 }
1197 1249
1198 void TabStrip::LayoutDraggedTabsAt(const std::vector<BaseTab*>& tabs, 1250 void TabStrip::LayoutDraggedTabsAt(const std::vector<BaseTab*>& tabs,
1199 BaseTab* active_tab, 1251 BaseTab* active_tab,
1200 const gfx::Point& location, 1252 const gfx::Point& location,
1201 bool initial_drag) { 1253 bool initial_drag) {
1202 if (stacking_) { 1254 if (touch_layout_.get()) {
1203 LayoutDraggedTabsAtWithStacking(active_tab, location); 1255 touch_layout_->DragActiveTab(location.x() - active_tab->bounds().x());
1256 DoLayout();
1204 return; 1257 return;
1205 } 1258 }
1206 // Immediately hide the new tab button if the last tab is being dragged. 1259 // Immediately hide the new tab button if the last tab is being dragged.
1207 if (tab_at(tab_count() - 1)->dragging()) 1260 if (tab_at(tab_count() - 1)->dragging())
1208 newtab_button_->SetVisible(false); 1261 newtab_button_->SetVisible(false);
1209 std::vector<gfx::Rect> bounds; 1262 std::vector<gfx::Rect> bounds;
1210 CalculateBoundsForDraggedTabs(tabs, &bounds); 1263 CalculateBoundsForDraggedTabs(tabs, &bounds);
1211 DCHECK_EQ(tabs.size(), bounds.size()); 1264 DCHECK_EQ(tabs.size(), bounds.size());
1212 int active_tab_model_index = GetModelIndexOfBaseTab(active_tab); 1265 int active_tab_model_index = GetModelIndexOfBaseTab(active_tab);
1213 int active_tab_index = static_cast<int>( 1266 int active_tab_index = static_cast<int>(
(...skipping 10 matching lines...) Expand all
1224 if ((initial_drag && 1277 if ((initial_drag &&
1225 GetModelIndexOfBaseTab(tabs[i]) != consecutive_index) || 1278 GetModelIndexOfBaseTab(tabs[i]) != consecutive_index) ||
1226 bounds_animator_.IsAnimating(tabs[i])) { 1279 bounds_animator_.IsAnimating(tabs[i])) {
1227 bounds_animator_.SetTargetBounds(tabs[i], new_bounds); 1280 bounds_animator_.SetTargetBounds(tabs[i], new_bounds);
1228 } else { 1281 } else {
1229 tab->SetBoundsRect(new_bounds); 1282 tab->SetBoundsRect(new_bounds);
1230 } 1283 }
1231 } 1284 }
1232 } 1285 }
1233 1286
1234 void TabStrip::LayoutDraggedTabsAtWithStacking(
1235 BaseTab* active_tab,
1236 const gfx::Point& location) {
1237 // TODO: this is wrong.
1238 active_tab->SetX(location.x());
1239 }
1240
1241 void TabStrip::CalculateBoundsForDraggedTabs(const std::vector<BaseTab*>& tabs, 1287 void TabStrip::CalculateBoundsForDraggedTabs(const std::vector<BaseTab*>& tabs,
1242 std::vector<gfx::Rect>* bounds) { 1288 std::vector<gfx::Rect>* bounds) {
1243 int x = 0; 1289 int x = 0;
1244 for (size_t i = 0; i < tabs.size(); ++i) { 1290 for (size_t i = 0; i < tabs.size(); ++i) {
1245 BaseTab* tab = tabs[i]; 1291 BaseTab* tab = tabs[i];
1246 if (i > 0 && tab->data().mini != tabs[i - 1]->data().mini) 1292 if (i > 0 && tab->data().mini != tabs[i - 1]->data().mini)
1247 x += kMiniToNonMiniGap; 1293 x += kMiniToNonMiniGap;
1248 gfx::Rect new_bounds = tab->bounds(); 1294 gfx::Rect new_bounds = tab->bounds();
1249 new_bounds.set_origin(gfx::Point(x, 0)); 1295 new_bounds.set_origin(gfx::Point(x, 0));
1250 bounds->push_back(new_bounds); 1296 bounds->push_back(new_bounds);
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
1372 // dragging true for the tab otherwise it'll draw beneath the new tab button. 1418 // dragging true for the tab otherwise it'll draw beneath the new tab button.
1373 bounds_animator_.SetAnimationDelegate( 1419 bounds_animator_.SetAnimationDelegate(
1374 tab, new ResetDraggingStateDelegate(tab), true); 1420 tab, new ResetDraggingStateDelegate(tab), true);
1375 } 1421 }
1376 1422
1377 void TabStrip::OwnDragController(TabDragController* controller) { 1423 void TabStrip::OwnDragController(TabDragController* controller) {
1378 drag_controller_.reset(controller); 1424 drag_controller_.reset(controller);
1379 } 1425 }
1380 1426
1381 void TabStrip::DestroyDragController() { 1427 void TabStrip::DestroyDragController() {
1382 if (newtab_button_) 1428 newtab_button_->SetVisible(true);
1383 newtab_button_->SetVisible(true);
1384 drag_controller_.reset(); 1429 drag_controller_.reset();
1385 } 1430 }
1386 1431
1387 TabDragController* TabStrip::ReleaseDragController() { 1432 TabDragController* TabStrip::ReleaseDragController() {
1388 return drag_controller_.release(); 1433 return drag_controller_.release();
1389 } 1434 }
1390 1435
1391 int TabStrip::NumNonClosingTabs(int index1, int index2) const {
1392 int count = 0;
1393 for (int i = index1; i < index2; ++i) {
1394 if (!tab_at(i)->closing())
1395 count++;
1396 }
1397 return count;
1398 }
1399
1400 void TabStrip::UpdateNumVisibleTabs(int non_closing_tab_count,
1401 int width,
1402 double tab_size) {
1403 num_visible_tabs_ = (width + kTabHOffset) / (tab_size + kTabHOffset);
1404 num_visible_tabs_ = std::max(1, num_visible_tabs_);
1405 // Make sure the first_visible_tab_index_ is valid.
1406 // TODO: this isn't quite right, it doesn't take into account closing tabs.
1407 first_visible_tab_index_ =
1408 std::min(first_visible_tab_index_, non_closing_tab_count -
1409 num_visible_tabs_);
1410 first_visible_tab_index_ = std::max(0, first_visible_tab_index_);
1411 }
1412
1413 void TabStrip::EnsureModelIndexIsVisible(int model_index) {
1414 if (!stacking_ || model_index == TabStripSelectionModel::kUnselectedIndex)
1415 return;
1416
1417 int original = first_visible_tab_index_;
1418 int active_index = model_index;
1419 if (active_index < first_visible_tab_index_)
1420 first_visible_tab_index_ = active_index;
1421 else if (active_index >= first_visible_tab_index_ + num_visible_tabs_)
1422 first_visible_tab_index_ = active_index - num_visible_tabs_ + 1;
1423
1424 if (original != first_visible_tab_index_) {
1425 PrepareForAnimation();
1426 GenerateIdealBounds();
1427 AnimateToIdealBounds();
1428 }
1429 }
1430
1431 void TabStrip::GetDesiredTabWidths(int tab_count, 1436 void TabStrip::GetDesiredTabWidths(int tab_count,
1432 int mini_tab_count, 1437 int mini_tab_count,
1433 double* unselected_width, 1438 double* unselected_width,
1434 double* selected_width) const { 1439 double* selected_width) const {
1435 DCHECK(tab_count >= 0 && mini_tab_count >= 0 && mini_tab_count <= tab_count); 1440 DCHECK(tab_count >= 0 && mini_tab_count >= 0 && mini_tab_count <= tab_count);
1436 const double min_unselected_width = Tab::GetMinimumUnselectedSize().width(); 1441 const double min_unselected_width = Tab::GetMinimumUnselectedSize().width();
1437 const double min_selected_width = Tab::GetMinimumSelectedSize().width(); 1442 const double min_selected_width = Tab::GetMinimumSelectedSize().width();
1438 1443
1439 *unselected_width = min_unselected_width; 1444 *unselected_width = min_unselected_width;
1440 *selected_width = min_selected_width; 1445 *selected_width = min_selected_width;
1441 1446
1442 if (tab_count == 0) { 1447 if (tab_count == 0) {
1443 // Return immediately to avoid divide-by-zero below. 1448 // Return immediately to avoid divide-by-zero below.
1444 return; 1449 return;
1445 } 1450 }
1446 1451
1447 // Determine how much space we can actually allocate to tabs. 1452 // Determine how much space we can actually allocate to tabs.
1448 int available_width; 1453 int available_width;
1449 if (available_width_for_tabs_ < 0) { 1454 if (available_width_for_tabs_ < 0) {
1450 available_width = width(); 1455 available_width = width() - new_tab_button_width();
1451 available_width -= (kNewTabButtonHOffset + newtab_button_bounds_.width());
1452 } else { 1456 } else {
1453 // Interesting corner case: if |available_width_for_tabs_| > the result 1457 // Interesting corner case: if |available_width_for_tabs_| > the result
1454 // of the calculation in the conditional arm above, the strip is in 1458 // of the calculation in the conditional arm above, the strip is in
1455 // overflow. We can either use the specified width or the true available 1459 // overflow. We can either use the specified width or the true available
1456 // width here; the first preserves the consistent "leave the last tab under 1460 // width here; the first preserves the consistent "leave the last tab under
1457 // the user's mouse so they can close many tabs" behavior at the cost of 1461 // the user's mouse so they can close many tabs" behavior at the cost of
1458 // prolonging the glitchy appearance of the overflow state, while the second 1462 // prolonging the glitchy appearance of the overflow state, while the second
1459 // gets us out of overflow as soon as possible but forces the user to move 1463 // gets us out of overflow as soon as possible but forces the user to move
1460 // their mouse for a few tabs' worth of closing. We choose visual 1464 // their mouse for a few tabs' worth of closing. We choose visual
1461 // imperfection over behavioral imperfection and select the first option. 1465 // imperfection over behavioral imperfection and select the first option.
(...skipping 249 matching lines...) Expand 10 before | Expand all | Expand 10 after
1711 } 1715 }
1712 1716
1713 void TabStrip::PrepareForAnimation() { 1717 void TabStrip::PrepareForAnimation() {
1714 if (!IsDragSessionActive() && !TabDragController::IsAttachedTo(this)) { 1718 if (!IsDragSessionActive() && !TabDragController::IsAttachedTo(this)) {
1715 for (int i = 0; i < tab_count(); ++i) 1719 for (int i = 0; i < tab_count(); ++i)
1716 tab_at(i)->set_dragging(false); 1720 tab_at(i)->set_dragging(false);
1717 } 1721 }
1718 } 1722 }
1719 1723
1720 void TabStrip::GenerateIdealBounds() { 1724 void TabStrip::GenerateIdealBounds() {
1721 int non_closing_tab_count = 0; 1725 int new_tab_y = SizeTabButtonToTopOfTabStrip() ? 0 : kNewTabButtonVOffset;
1722 int mini_tab_count = 0; 1726
1723 for (int i = 0; i < tab_count(); ++i) { 1727 if (touch_layout_.get()) {
1724 BaseTab* tab = tab_at(i); 1728 if (tabs_.view_size() == 0)
1725 if (!tab->closing()) { 1729 return;
1726 ++non_closing_tab_count; 1730
1727 if (tab->data().mini) 1731 int new_tab_x = tabs_.ideal_bounds(tabs_.view_size() - 1).right() +
1728 mini_tab_count++; 1732 kNewTabButtonHOffset;
1729 } 1733 newtab_button_bounds_.set_origin(gfx::Point(new_tab_x, new_tab_y));
1734 return;
1730 } 1735 }
1731 1736
1732 double unselected, selected; 1737 double unselected, selected;
1733 GetDesiredTabWidths(non_closing_tab_count, mini_tab_count, &unselected, 1738 GetDesiredTabWidths(tab_count(), GetMiniTabCount(), &unselected, &selected);
1734 &selected);
1735
1736 current_unselected_width_ = unselected; 1739 current_unselected_width_ = unselected;
1737 current_selected_width_ = selected; 1740 current_selected_width_ = selected;
1738 1741
1739 // NOTE: This currently assumes a tab's height doesn't differ based on 1742 // NOTE: This currently assumes a tab's height doesn't differ based on
1740 // selected state or the number of tabs in the strip! 1743 // selected state or the number of tabs in the strip!
1741 int tab_height = Tab::GetStandardSize().height(); 1744 int tab_height = Tab::GetStandardSize().height();
1742 double tab_x = 0; 1745 int first_non_mini_index = 0;
1743 bool last_was_mini = false; 1746 double tab_x = GenerateIdealBoundsForMiniTabs(&first_non_mini_index);
1744 for (int i = 0; i < tab_count(); ++i) { 1747 for (int i = first_non_mini_index; i < tab_count(); ++i) {
1745 Tab* tab = tab_at(i); 1748 Tab* tab = tab_at(i);
1746 if (!tab->closing()) { 1749 DCHECK(!tab->data().mini);
1747 double tab_width = unselected; 1750 double tab_width = tab->IsActive() ? selected : unselected;
1748 if (tab->data().mini) { 1751 double end_of_tab = tab_x + tab_width;
1749 tab_width = Tab::GetMiniWidth(); 1752 int rounded_tab_x = Round(tab_x);
1750 } else { 1753 set_ideal_bounds(
1751 if (last_was_mini) { 1754 i,
1752 // Give a bigger gap between mini and non-mini tabs. 1755 gfx::Rect(rounded_tab_x, 0, Round(end_of_tab) - rounded_tab_x,
1753 tab_x += kMiniToNonMiniGap; 1756 tab_height));
1754 } 1757 tab_x = end_of_tab + kTabHOffset;
1755 if (tab->IsActive())
1756 tab_width = selected;
1757 }
1758 double end_of_tab = tab_x + tab_width;
1759 int rounded_tab_x = Round(tab_x);
1760 set_ideal_bounds(i,
1761 gfx::Rect(rounded_tab_x, 0, Round(end_of_tab) - rounded_tab_x,
1762 tab_height));
1763 tab_x = end_of_tab + kTabHOffset;
1764 last_was_mini = tab->data().mini;
1765 }
1766 }
1767
1768 // If we're in stacking mode, update the positions now that we have
1769 // appropriate widths.
1770 if (stacking_ && tab_x - kTabHOffset >
1771 width() - newtab_button_bounds_.width()) {
1772 tab_x = GenerateIdealBoundsWithStacking(
1773 mini_tab_count, non_closing_tab_count, tab_x, selected);
1774 } 1758 }
1775 1759
1776 // Update bounds of new tab button. 1760 // Update bounds of new tab button.
1777 int new_tab_x; 1761 int new_tab_x;
1778 int new_tab_y = SizeTabButtonToTopOfTabStrip() ? 0 : kNewTabButtonVOffset;
1779 if (abs(Round(unselected) - Tab::GetStandardSize().width()) > 1 && 1762 if (abs(Round(unselected) - Tab::GetStandardSize().width()) > 1 &&
1780 !in_tab_close_) { 1763 !in_tab_close_) {
1781 // We're shrinking tabs, so we need to anchor the New Tab button to the 1764 // We're shrinking tabs, so we need to anchor the New Tab button to the
1782 // right edge of the TabStrip's bounds, rather than the right edge of the 1765 // right edge of the TabStrip's bounds, rather than the right edge of the
1783 // right-most Tab, otherwise it'll bounce when animating. 1766 // right-most Tab, otherwise it'll bounce when animating.
1784 new_tab_x = width() - newtab_button_bounds_.width(); 1767 new_tab_x = width() - newtab_button_bounds_.width();
1785 } else { 1768 } else {
1786 new_tab_x = Round(tab_x - kTabHOffset) + kNewTabButtonHOffset; 1769 new_tab_x = Round(tab_x - kTabHOffset) + kNewTabButtonHOffset;
1787 } 1770 }
1788 newtab_button_bounds_.set_origin(gfx::Point(new_tab_x, new_tab_y)); 1771 newtab_button_bounds_.set_origin(gfx::Point(new_tab_x, new_tab_y));
1789 } 1772 }
1790 1773
1791 double TabStrip::GenerateIdealBoundsWithStacking(int mini_tab_count, 1774 int TabStrip::GenerateIdealBoundsForMiniTabs(int* first_non_mini_index) {
1792 int non_closing_tab_count, 1775 int next_x = 0;
1793 double new_tab_x, 1776 int mini_width = Tab::GetMiniWidth();
1794 double selected_size) { 1777 int tab_height = Tab::GetStandardSize().height();
1795 if (non_closing_tab_count == mini_tab_count) 1778 int index = 0;
1796 return new_tab_x; 1779 for (; index < tab_count() && tab_at(index)->data().mini; ++index) {
1780 set_ideal_bounds(index,
1781 gfx::Rect(next_x, 0, mini_width, tab_height));
1782 next_x += mini_width + kTabHOffset;
1783 }
1784 if (index > 0 && index < tab_count())
1785 next_x += kMiniToNonMiniGap;
1786 if (first_non_mini_index)
1787 *first_non_mini_index = index;
1788 return next_x;
1789 }
1797 1790
1798 // Pinned tabs always have their desired size, and don't stack. We rely on 1791 int TabStrip::new_tab_button_width() const {
1799 // GenerateIdealBounds to have positioned pinned tabs already, so don't move 1792 return kNewTabButtonWidth + kNewTabButtonHOffset;
1800 // them.
1801 int tab_index = 0;
1802 double next_x = 0;
1803 for (; tab_index < tab_count(); ++tab_index) {
1804 BaseTab* tab = tab_at(tab_index);
1805 if (!tab->closing() && !tab->data().mini) {
1806 next_x = ideal_bounds(tab_index).x();
1807 break;
1808 }
1809 }
1810 if (tab_index == tab_count())
1811 return new_tab_x;
1812
1813 // Always reserve kMaxEdgeStackWidth on each side for stacking. We don't
1814 // later try to adjust as it can lead to tabs bouncing around.
1815 int available_width = width() -
1816 (kNewTabButtonHOffset + newtab_button_bounds_.width()) - next_x -
1817 2 * kMaxEdgeStackWidth;
1818 if (available_width <= 0)
1819 return new_tab_x;
1820 UpdateNumVisibleTabs(non_closing_tab_count, available_width, selected_size);
1821 int stacked_leading_index = tab_index;
1822 int stacked_trailing_index = first_visible_tab_index_;
1823 for (int visible_count = 0;
1824 stacked_trailing_index < tab_count() &&
1825 visible_count < num_visible_tabs_; ++stacked_trailing_index) {
1826 if (!tab_at(stacked_trailing_index)->closing())
1827 visible_count++;
1828 }
1829 int stacked_leading_count =
1830 NumNonClosingTabs(stacked_leading_index, first_visible_tab_index_);
1831 int stacked_trailing_count =
1832 NumNonClosingTabs(stacked_trailing_index, tab_count());
1833
1834 // Stacked tabs to the left.
1835 if (stacked_leading_count > 0) {
1836 double stacked_offset =
1837 kMaxEdgeStackWidth / static_cast<double>(stacked_leading_count);
1838 for (; tab_index < first_visible_tab_index_; ++tab_index) {
1839 if (tab_at(tab_index)->closing())
1840 continue;
1841 gfx::Rect bounds = ideal_bounds(tab_index);
1842 bounds.set_x(next_x);
1843 set_ideal_bounds(tab_index, bounds);
1844 next_x += stacked_offset;
1845 }
1846 } else {
1847 tab_index = first_visible_tab_index_;
1848 }
1849
1850 if (stacked_leading_count > 0 && stacked_trailing_count == 0) {
1851 // If there are no stacked tabs on the right and there is extra space, give
1852 // it to the last stacked tab on the left.
1853 next_x += available_width + kMaxEdgeStackWidth -
1854 (num_visible_tabs_ * selected_size +
1855 std::max(num_visible_tabs_ - 1, 0) * kTabHOffset);
1856 }
1857
1858 // Totally visible tabs.
1859 for (int visible_count = 0;
1860 tab_index < tab_count() && visible_count < num_visible_tabs_;
1861 tab_index++) {
1862 if (tab_at(tab_index)->closing())
1863 continue;
1864 gfx::Rect bounds = ideal_bounds(tab_index);
1865 bounds.set_x(next_x);
1866 next_x = bounds.right() + kTabHOffset;
1867 set_ideal_bounds(tab_index, bounds);
1868 visible_count++;
1869 }
1870
1871 // Stacked tabs to the right.
1872 if (stacked_trailing_count == 0)
1873 return next_x;
1874
1875 double stacked_offset =
1876 kMaxEdgeStackWidth / static_cast<double>(stacked_trailing_count);
1877 next_x = width() - (kNewTabButtonHOffset + newtab_button_bounds_.width()) -
1878 (stacked_trailing_count - 1) * stacked_offset - selected_size;
1879 for (; tab_index < tab_count(); ++tab_index) {
1880 if (tab_at(tab_index)->closing())
1881 continue;
1882 gfx::Rect bounds = ideal_bounds(tab_index);
1883 bounds.set_x(next_x);
1884 set_ideal_bounds(tab_index, bounds);
1885 next_x += stacked_offset;
1886 }
1887
1888 return next_x + kTabHOffset;
1889 } 1793 }
1890 1794
1891 void TabStrip::StartResizeLayoutAnimation() { 1795 void TabStrip::StartResizeLayoutAnimation() {
1892 PrepareForAnimation(); 1796 PrepareForAnimation();
1893 GenerateIdealBounds(); 1797 GenerateIdealBounds();
1894 AnimateToIdealBounds(); 1798 AnimateToIdealBounds();
1895 } 1799 }
1896 1800
1897 void TabStrip::StartMiniTabAnimation() { 1801 void TabStrip::StartMiniTabAnimation() {
1898 in_tab_close_ = false; 1802 in_tab_close_ = false;
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
1946 new RemoveTabDelegate(this, tab_closing), 1850 new RemoveTabDelegate(this, tab_closing),
1947 true); 1851 true);
1948 } 1852 }
1949 1853
1950 bool TabStrip::IsPointInTab(Tab* tab, 1854 bool TabStrip::IsPointInTab(Tab* tab,
1951 const gfx::Point& point_in_tabstrip_coords) { 1855 const gfx::Point& point_in_tabstrip_coords) {
1952 gfx::Point point_in_tab_coords(point_in_tabstrip_coords); 1856 gfx::Point point_in_tab_coords(point_in_tabstrip_coords);
1953 View::ConvertPointToView(this, tab, &point_in_tab_coords); 1857 View::ConvertPointToView(this, tab, &point_in_tab_coords);
1954 return tab->HitTest(point_in_tab_coords); 1858 return tab->HitTest(point_in_tab_coords);
1955 } 1859 }
1860
1861 int TabStrip::GetStartXForNormalTabs() const {
1862 int mini_tab_count = GetMiniTabCount();
1863 if (mini_tab_count == 0)
1864 return 0;
1865 return mini_tab_count * (Tab::GetMiniWidth() + kTabHOffset) +
1866 kMiniToNonMiniGap;
1867 }
1868
1869 Tab* TabStrip::FindTabForEvent(const gfx::Point& point, int start, int delta) {
1870 for (int i = start; i >= 0 && i < tab_count(); i += delta) {
1871 if (IsPointInTab(tab_at(i), point))
1872 return tab_at(i);
1873 }
1874 return NULL;
1875 }
OLDNEW
« no previous file with comments | « chrome/browser/ui/views/tabs/tab_strip.h ('k') | chrome/browser/ui/views/tabs/tab_strip_controller.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698