OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 } |
OLD | NEW |