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

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

Issue 5025: Scrolling through tabs by using mouse-wheel on tab bar Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: '' Created 12 years, 2 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
« no previous file with comments | « chrome/browser/views/tabs/tab_strip.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2006-2008 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/views/tabs/tab_strip.h" 5 #include "chrome/browser/views/tabs/tab_strip.h"
6 6
7 #include "base/gfx/size.h" 7 #include "base/gfx/size.h"
8 #include "chrome/app/theme/theme_resources.h" 8 #include "chrome/app/theme/theme_resources.h"
9 #include "chrome/browser/profile.h" 9 #include "chrome/browser/profile.h"
10 #include "chrome/browser/tab_contents.h" 10 #include "chrome/browser/tab_contents.h"
(...skipping 424 matching lines...) Expand 10 before | Expand all | Expand 10 after
435 : TabAnimation(tabstrip, RESIZE) { 435 : TabAnimation(tabstrip, RESIZE) {
436 int tab_count = tabstrip->GetTabCount(); 436 int tab_count = tabstrip->GetTabCount();
437 GenerateStartAndEndWidths(tab_count, tab_count); 437 GenerateStartAndEndWidths(tab_count, tab_count);
438 InitStartState(); 438 InitStartState();
439 } 439 }
440 virtual ~ResizeLayoutAnimation() { 440 virtual ~ResizeLayoutAnimation() {
441 } 441 }
442 442
443 // Overridden from AnimationDelegate: 443 // Overridden from AnimationDelegate:
444 virtual void AnimationEnded(const Animation* animation) { 444 virtual void AnimationEnded(const Animation* animation) {
445 tabstrip_->resize_layout_scheduled_ = false; 445 tabstrip_->needs_resize_layout_ = false;
446 TabStrip::TabAnimation::AnimationEnded(animation); 446 TabStrip::TabAnimation::AnimationEnded(animation);
447 } 447 }
448 448
449 protected: 449 protected:
450 // Overridden from TabStrip::TabAnimation: 450 // Overridden from TabStrip::TabAnimation:
451 virtual int GetDuration() const { 451 virtual int GetDuration() const {
452 return kResizeLayoutAnimationDurationMs; 452 return kResizeLayoutAnimationDurationMs;
453 } 453 }
454 454
455 virtual double GetWidthForTab(int index) const { 455 virtual double GetWidthForTab(int index) const {
(...skipping 24 matching lines...) Expand all
480 DISALLOW_EVIL_CONSTRUCTORS(ResizeLayoutAnimation); 480 DISALLOW_EVIL_CONSTRUCTORS(ResizeLayoutAnimation);
481 }; 481 };
482 482
483 /////////////////////////////////////////////////////////////////////////////// 483 ///////////////////////////////////////////////////////////////////////////////
484 // TabStrip, public: 484 // TabStrip, public:
485 485
486 TabStrip::TabStrip(TabStripModel* model) 486 TabStrip::TabStrip(TabStripModel* model)
487 : model_(model), 487 : model_(model),
488 resize_layout_factory_(this), 488 resize_layout_factory_(this),
489 added_as_message_loop_observer_(false), 489 added_as_message_loop_observer_(false),
490 resize_layout_scheduled_(false), 490 needs_resize_layout_(false),
491 current_unselected_width_(Tab::GetStandardSize().width()), 491 current_unselected_width_(Tab::GetStandardSize().width()),
492 current_selected_width_(Tab::GetStandardSize().width()), 492 current_selected_width_(Tab::GetStandardSize().width()),
493 available_width_for_tabs_(-1) { 493 available_width_for_tabs_(-1) {
494 Init(); 494 Init();
495 } 495 }
496 496
497 TabStrip::~TabStrip() { 497 TabStrip::~TabStrip() {
498 // TODO(beng): (1031854) Restore this line once XPFrame/VistaFrame are dead. 498 // TODO(beng): (1031854) Restore this line once XPFrame/VistaFrame are dead.
499 //model_->RemoveObserver(this); 499 //model_->RemoveObserver(this);
500 500
(...skipping 361 matching lines...) Expand 10 before | Expand all | Expand 10 after
862 862
863 void TabStrip::TabSelectedAt(TabContents* old_contents, 863 void TabStrip::TabSelectedAt(TabContents* old_contents,
864 TabContents* new_contents, 864 TabContents* new_contents,
865 int index, 865 int index,
866 bool user_gesture) { 866 bool user_gesture) {
867 DCHECK(index >= 0 && index < GetTabCount()); 867 DCHECK(index >= 0 && index < GetTabCount());
868 if (CanUpdateDisplay()) { 868 if (CanUpdateDisplay()) {
869 // We have "tiny tabs" if the tabs are so tiny that the unselected ones are 869 // We have "tiny tabs" if the tabs are so tiny that the unselected ones are
870 // a different size to the selected ones. 870 // a different size to the selected ones.
871 bool tiny_tabs = current_unselected_width_ != current_selected_width_; 871 bool tiny_tabs = current_unselected_width_ != current_selected_width_;
872 if (!IsAnimating() && (!resize_layout_scheduled_ || tiny_tabs)) { 872 if (!IsAnimating() && (!needs_resize_layout_ || tiny_tabs)) {
873 Layout(); 873 Layout();
874 } else { 874 } else {
875 SchedulePaint(); 875 SchedulePaint();
876 } 876 }
877 } 877 }
878 } 878 }
879 879
880 void TabStrip::TabMoved(TabContents* contents, int from_index, int to_index) { 880 void TabStrip::TabMoved(TabContents* contents, int from_index, int to_index) {
881 Tab* tab = GetTabAt(from_index); 881 Tab* tab = GetTabAt(from_index);
882 Tab* other_tab = GetTabAt(to_index); 882 Tab* other_tab = GetTabAt(to_index);
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
940 int tab_index = GetIndexOfTab(tab); 940 int tab_index = GetIndexOfTab(tab);
941 if (model_->ContainsIndex(tab_index)) { 941 if (model_->ContainsIndex(tab_index)) {
942 TabContents* contents = model_->GetTabContentsAt(tab_index); 942 TabContents* contents = model_->GetTabContentsAt(tab_index);
943 if (contents) 943 if (contents)
944 UserMetrics::RecordAction(L"CloseTab_Mouse", contents->profile()); 944 UserMetrics::RecordAction(L"CloseTab_Mouse", contents->profile());
945 Tab* last_tab = GetTabAt(GetTabCount() - 1); 945 Tab* last_tab = GetTabAt(GetTabCount() - 1);
946 // Limit the width available to the TabStrip for laying out Tabs, so that 946 // Limit the width available to the TabStrip for laying out Tabs, so that
947 // Tabs are not resized until a later time (when the mouse pointer leaves 947 // Tabs are not resized until a later time (when the mouse pointer leaves
948 // the TabStrip). 948 // the TabStrip).
949 available_width_for_tabs_ = GetAvailableWidthForTabs(last_tab); 949 available_width_for_tabs_ = GetAvailableWidthForTabs(last_tab);
950 resize_layout_scheduled_ = true; 950 needs_resize_layout_ = true;
951 AddMessageLoopObserver();
952 model_->CloseTabContentsAt(tab_index); 951 model_->CloseTabContentsAt(tab_index);
953 } 952 }
954 } 953 }
955 954
956 bool TabStrip::IsCommandEnabledForTab( 955 bool TabStrip::IsCommandEnabledForTab(
957 TabStripModel::ContextMenuCommand command_id, const Tab* tab) const { 956 TabStripModel::ContextMenuCommand command_id, const Tab* tab) const {
958 int index = GetIndexOfTab(tab); 957 int index = GetIndexOfTab(tab);
959 if (model_->ContainsIndex(index)) 958 if (model_->ContainsIndex(index))
960 return model_->IsContextMenuCommandEnabled(index, command_id); 959 return model_->IsContextMenuCommandEnabled(index, command_id);
961 return false; 960 return false;
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
1046 model_->AddBlankTab(true); 1045 model_->AddBlankTab(true);
1047 } 1046 }
1048 1047
1049 /////////////////////////////////////////////////////////////////////////////// 1048 ///////////////////////////////////////////////////////////////////////////////
1050 // TabStrip, MessageLoop::Observer implementation: 1049 // TabStrip, MessageLoop::Observer implementation:
1051 1050
1052 void TabStrip::WillProcessMessage(const MSG& msg) { 1051 void TabStrip::WillProcessMessage(const MSG& msg) {
1053 } 1052 }
1054 1053
1055 void TabStrip::DidProcessMessage(const MSG& msg) { 1054 void TabStrip::DidProcessMessage(const MSG& msg) {
1056 // We spy on three different Windows messages here to see if the mouse has
1057 // moved out of the bounds of the tabstrip, which we use as our cue to kick
1058 // of the resize animation. The messages are:
1059 //
1060 // WM_MOUSEMOVE:
1061 // For when the mouse moves from the tabstrip over into the rest of the
1062 // browser UI, i.e. within the bounds of the same windows HWND.
1063 // WM_MOUSELEAVE:
1064 // For when the mouse moves very rapidly from a tab closed in the middle of
1065 // the tabstrip (_not_ the end) out of the bounds of the browser's HWND and
1066 // over some other HWND.
1067 // WM_NCMOUSELEAVE:
1068 // For when the mouse moves very rapidly from the end of the tabstrip (when
1069 // the last tab is closed and the mouse is left floating over the title
1070 // bar). Because the empty area of the tabstrip at the end of the title bar
1071 // is registered by the ChromeFrame as part of the "caption" area of the
1072 // window (the frame's OnNCHitTest method returns HTCAPTION for this
1073 // region), the frame's HWND receives a WM_MOUSEMOVE message immediately,
1074 // because as far as it is concerned the mouse has _left_ the client area
1075 // of the window (and is now over the non-client area). To be notified
1076 // again when the mouse leaves the _non-client_ area, we use the
1077 // WM_NCMOUSELEAVE message, which causes us to re-evaluate the cursor
1078 // position and correctly resize the tabstrip.
1079 //
1080 switch (msg.message) { 1055 switch (msg.message) {
1056 // We spy on three different Windows messages here to see if the mouse has
1057 // moved out of the bounds of the tabstrip, which we use as our cue to kick
1058 // of the resize animation. The messages are:
1059 //
1060 // WM_MOUSEMOVE:
1061 // For when the mouse moves from the tabstrip over into the rest of the
1062 // browser UI, i.e. within the bounds of the same windows HWND.
1063 // WM_MOUSELEAVE:
1064 // For when the mouse moves very rapidly from a tab closed in the middle
1065 // of the tabstrip (_not_ the end) out of the bounds of the browser's
1066 // HWND and over some other HWND.
1067 // WM_NCMOUSELEAVE:
1068 // For when the mouse moves very rapidly from the end of the tabstrip
1069 // (when the last tab is closed and the mouse is left floating over the
1070 // title bar). Because the empty area of the tabstrip at the end of the
1071 // title bar is registered by the ChromeFrame as part of the "caption"
1072 // area of the window (the frame's OnNCHitTest method returns HTCAPTION
1073 // for this region), the frame's HWND receives a WM_MOUSEMOVE message
1074 // immediately, because as far as it is concerned the mouse has _left_
1075 // the client area of the window (and is now over the non-client area).
1076 // To be notified again when the mouse leaves the _non-client_ area, we
1077 // use the WM_NCMOUSELEAVE message, which causes us to re-evaluate the
1078 // cursor position and correctly resize the tabstrip.
1079 //
1081 case WM_MOUSEMOVE: 1080 case WM_MOUSEMOVE:
1082 case WM_MOUSELEAVE: 1081 case WM_MOUSELEAVE:
1083 case WM_NCMOUSELEAVE: 1082 case WM_NCMOUSELEAVE:
1083 if (!needs_resize_layout_)
1084 break;
1084 if (!IsCursorInTabStripZone()) { 1085 if (!IsCursorInTabStripZone()) {
1085 // Mouse moved outside the tab slop zone, start a timer to do a resize 1086 // Mouse moved outside the tab slop zone, start a timer to do a resize
1086 // layout after a short while... 1087 // layout after a short while...
1087 if (resize_layout_factory_.empty()) { 1088 if (resize_layout_factory_.empty()) {
1088 MessageLoop::current()->PostDelayedTask(FROM_HERE, 1089 MessageLoop::current()->PostDelayedTask(FROM_HERE,
1089 resize_layout_factory_.NewRunnableMethod( 1090 resize_layout_factory_.NewRunnableMethod(
1090 &TabStrip::ResizeLayoutTabs), 1091 &TabStrip::ResizeLayoutTabs),
1091 kResizeTabsTimeMs); 1092 kResizeTabsTimeMs);
1092 } 1093 }
1093 } else { 1094 } else {
1094 // Mouse moved quickly out of the tab strip and then into it again, so 1095 // Mouse moved quickly out of the tab strip and then into it again, so
1095 // cancel the timer so that the strip doesn't move when the mouse moves 1096 // cancel the timer so that the strip doesn't move when the mouse moves
1096 // back over it. 1097 // back over it.
1097 resize_layout_factory_.RevokeAll(); 1098 resize_layout_factory_.RevokeAll();
1098 } 1099 }
1099 break; 1100 break;
1101 // We also watch out for mouse-wheel messages here to allow the user to
1102 // leaf through the tabs by scrolling while the mouse cursor is in the tab
1103 // strip zone.
1104 case WM_MOUSEWHEEL:
1105 if (!IsCursorInTabStripZone())
1106 break;
1107 CHECK(model_);
1108 // A positive value indicates that the wheel was rotated away from the
1109 // user; a negative value indicates rotation toward the user.
1110 if (GET_WHEEL_DELTA_WPARAM(msg.wParam) > 0)
1111 model_->SelectPreviousTab();
1112 else
1113 model_->SelectNextTab();
1114 break;
1100 } 1115 }
1101 } 1116 }
1102 1117
1103 /////////////////////////////////////////////////////////////////////////////// 1118 ///////////////////////////////////////////////////////////////////////////////
1104 // TabStrip, private: 1119 // TabStrip, private:
1105 1120
1106 void TabStrip::Init() { 1121 void TabStrip::Init() {
1107 model_->AddObserver(this); 1122 model_->AddObserver(this);
1108 newtab_button_ = new NewTabButton; 1123 newtab_button_ = new NewTabButton;
1109 newtab_button_->SetListener(this, TabStripModel::kNoTab); 1124 newtab_button_->SetListener(this, TabStripModel::kNoTab);
(...skipping 12 matching lines...) Expand all
1122 1137
1123 newtab_button_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_NEWTAB)); 1138 newtab_button_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_NEWTAB));
1124 AddChildView(newtab_button_); 1139 AddChildView(newtab_button_);
1125 1140
1126 if (drop_indicator_width == 0) { 1141 if (drop_indicator_width == 0) {
1127 // Direction doesn't matter, both images are the same size. 1142 // Direction doesn't matter, both images are the same size.
1128 SkBitmap* drop_image = GetDropArrowImage(true); 1143 SkBitmap* drop_image = GetDropArrowImage(true);
1129 drop_indicator_width = drop_image->width(); 1144 drop_indicator_width = drop_image->width();
1130 drop_indicator_height = drop_image->height(); 1145 drop_indicator_height = drop_image->height();
1131 } 1146 }
1147
1148 AddMessageLoopObserver();
1132 } 1149 }
1133 1150
1134 Tab* TabStrip::GetTabAt(int index) const { 1151 Tab* TabStrip::GetTabAt(int index) const {
1135 DCHECK(index >= 0 && index < GetTabCount()); 1152 DCHECK(index >= 0 && index < GetTabCount());
1136 return tab_data_.at(index).tab; 1153 return tab_data_.at(index).tab;
1137 } 1154 }
1138 1155
1139 Tab* TabStrip::GetTabAtAdjustForAnimation(int index) const { 1156 Tab* TabStrip::GetTabAtAdjustForAnimation(int index) const {
1140 if (active_animation_.get() && 1157 if (active_animation_.get() &&
1141 active_animation_->type() == TabAnimation::REMOVE && 1158 active_animation_->type() == TabAnimation::REMOVE &&
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
1217 // Selected width = (total width - (unselected width * (num_tabs - 1))) 1234 // Selected width = (total width - (unselected width * (num_tabs - 1)))
1218 *selected_width = std::max(available_width - total_offset - 1235 *selected_width = std::max(available_width - total_offset -
1219 (min_unselected_width * (tab_count - 1)), min_selected_width); 1236 (min_unselected_width * (tab_count - 1)), min_selected_width);
1220 } 1237 }
1221 } 1238 }
1222 } 1239 }
1223 1240
1224 void TabStrip::ResizeLayoutTabs() { 1241 void TabStrip::ResizeLayoutTabs() {
1225 resize_layout_factory_.RevokeAll(); 1242 resize_layout_factory_.RevokeAll();
1226 1243
1227 // It is critically important that this is unhooked here, otherwise we will
1228 // keep spying on messages forever.
1229 RemoveMessageLoopObserver();
1230
1231 available_width_for_tabs_ = -1; 1244 available_width_for_tabs_ = -1;
1232 double unselected, selected; 1245 double unselected, selected;
1233 GetDesiredTabWidths(GetTabCount(), &unselected, &selected); 1246 GetDesiredTabWidths(GetTabCount(), &unselected, &selected);
1234 Tab* first_tab = GetTabAt(0); 1247 Tab* first_tab = GetTabAt(0);
1235 int w = Round(first_tab->IsSelected() ? selected : selected); 1248 int w = Round(first_tab->IsSelected() ? selected : selected);
1236 1249
1237 // We only want to run the animation if we're not already at the desired 1250 // We only want to run the animation if we're not already at the desired
1238 // size. 1251 // size.
1239 if (abs(first_tab->width() - w) > 1) 1252 if (abs(first_tab->width() - w) > 1)
1240 StartResizeLayoutAnimation(); 1253 StartResizeLayoutAnimation();
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after
1454 gfx::Rect state(rounded_tab_x, 0, Round(end_of_tab) - rounded_tab_x, 1467 gfx::Rect state(rounded_tab_x, 0, Round(end_of_tab) - rounded_tab_x,
1455 tab_height); 1468 tab_height);
1456 tab_data_.at(i).ideal_bounds = state; 1469 tab_data_.at(i).ideal_bounds = state;
1457 tab_x = end_of_tab + kTabHOffset; 1470 tab_x = end_of_tab + kTabHOffset;
1458 } 1471 }
1459 } 1472 }
1460 1473
1461 void TabStrip::LayoutNewTabButton(double last_tab_right, 1474 void TabStrip::LayoutNewTabButton(double last_tab_right,
1462 double unselected_width) { 1475 double unselected_width) {
1463 int delta = abs(Round(unselected_width) - Tab::GetStandardSize().width()); 1476 int delta = abs(Round(unselected_width) - Tab::GetStandardSize().width());
1464 if (delta > 1 && !resize_layout_scheduled_) { 1477 if (delta > 1 && !needs_resize_layout_) {
1465 // We're shrinking tabs, so we need to anchor the New Tab button to the 1478 // We're shrinking tabs, so we need to anchor the New Tab button to the
1466 // right edge of the TabStrip's bounds, rather than the right edge of the 1479 // right edge of the TabStrip's bounds, rather than the right edge of the
1467 // right-most Tab, otherwise it'll bounce when animating. 1480 // right-most Tab, otherwise it'll bounce when animating.
1468 newtab_button_->SetBounds(width() - newtab_button_size_.width(), 1481 newtab_button_->SetBounds(width() - newtab_button_size_.width(),
1469 kNewTabButtonVOffset, 1482 kNewTabButtonVOffset,
1470 newtab_button_size_.width(), 1483 newtab_button_size_.width(),
1471 newtab_button_size_.height()); 1484 newtab_button_size_.height());
1472 } else { 1485 } else {
1473 newtab_button_->SetBounds( 1486 newtab_button_->SetBounds(
1474 Round(last_tab_right - kTabHOffset) + kNewTabButtonHOffset, 1487 Round(last_tab_right - kTabHOffset) + kNewTabButtonHOffset,
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
1566 int TabStrip::GetAvailableWidthForTabs(Tab* last_tab) const { 1579 int TabStrip::GetAvailableWidthForTabs(Tab* last_tab) const {
1567 return last_tab->x() + last_tab->width(); 1580 return last_tab->x() + last_tab->width();
1568 } 1581 }
1569 1582
1570 bool TabStrip::IsPointInTab(Tab* tab, const CPoint& point_in_tabstrip_coords) { 1583 bool TabStrip::IsPointInTab(Tab* tab, const CPoint& point_in_tabstrip_coords) {
1571 CPoint point_in_tab_coords(point_in_tabstrip_coords); 1584 CPoint point_in_tab_coords(point_in_tabstrip_coords);
1572 View::ConvertPointToView(this, tab, &point_in_tab_coords); 1585 View::ConvertPointToView(this, tab, &point_in_tab_coords);
1573 return tab->HitTest(point_in_tab_coords); 1586 return tab->HitTest(point_in_tab_coords);
1574 } 1587 }
1575 1588
OLDNEW
« no previous file with comments | « chrome/browser/views/tabs/tab_strip.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698