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 "ui/views/controls/tabbed_pane/tabbed_pane.h" | 5 #include "ui/views/controls/tabbed_pane/tabbed_pane.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
8 #include "ui/base/accessibility/accessible_view_state.h" | 8 #include "ui/base/accessibility/accessible_view_state.h" |
9 #include "ui/base/keycodes/keyboard_codes.h" | 9 #include "ui/base/keycodes/keyboard_codes.h" |
10 #include "ui/views/controls/native/native_view_host.h" | 10 #include "ui/gfx/canvas.h" |
11 #include "ui/views/controls/tabbed_pane/native_tabbed_pane_views.h" | 11 #include "ui/gfx/font.h" |
12 #include "ui/views/controls/tabbed_pane/native_tabbed_pane_wrapper.h" | 12 #include "ui/views/controls/label.h" |
13 #include "ui/views/controls/tabbed_pane/tabbed_pane_listener.h" | 13 #include "ui/views/controls/tabbed_pane/tabbed_pane_listener.h" |
| 14 #include "ui/views/layout/layout_manager.h" |
14 #include "ui/views/widget/widget.h" | 15 #include "ui/views/widget/widget.h" |
15 | 16 |
16 // TODO(markusheintz): This should be removed once the native Windows tabbed | |
17 // pane is not used anymore (http://crbug.com/138059). | |
18 #if defined(OS_WIN) && !defined(USE_AURA) | |
19 #include "ui/views/controls/tabbed_pane/native_tabbed_pane_win.h" | |
20 #endif | |
21 | |
22 namespace { | 17 namespace { |
23 | 18 |
24 views::NativeTabbedPaneWrapper* CreateNativeWrapper( | 19 const char kViewClassName[] = "views/TabbedPane"; |
25 views::TabbedPane* tabbed_pane) { | 20 |
26 #if defined(OS_WIN) && !defined(USE_AURA) | 21 // TODO(markusheintz|msw): Use NativeTheme colors. |
27 if (tabbed_pane->use_native_win_control()) | 22 const SkColor kTabTitleColor_Inactive = SkColorSetRGB(0x66, 0x66, 0x66); |
28 return new views::NativeTabbedPaneWin(tabbed_pane); | 23 const SkColor kTabTitleColor_Active = SkColorSetRGB(0x20, 0x20, 0x20); |
29 #endif | 24 const SkColor kTabTitleColor_Pressed = SkColorSetRGB(0x33, 0x33, 0x33); |
30 return new views::NativeTabbedPaneViews(tabbed_pane); | 25 const SkColor kTabTitleColor_Hovered = SkColorSetRGB(0x22, 0x22, 0x22); |
31 } | 26 const SkColor kTabBackgroundColor = SK_ColorWHITE; |
| 27 const SkColor kTabBorderColor = SkColorSetRGB(0xCC, 0xCC, 0xCC); |
| 28 const SkScalar kTabBorderThickness = 1.0f; |
| 29 const SkScalar kTabBorderRadius = 2.0f; |
32 | 30 |
33 } // namespace | 31 } // namespace |
34 | 32 |
35 namespace views { | 33 namespace views { |
36 | 34 |
37 // static | 35 // The tab view shown in the tab strip. |
38 const char TabbedPane::kViewClassName[] = "views/TabbedPane"; | 36 class Tab : public View { |
| 37 public: |
| 38 Tab(TabbedPane* tabbed_pane, const string16& title, View* contents); |
| 39 virtual ~Tab(); |
| 40 |
| 41 View* contents() const { return contents_; } |
| 42 |
| 43 bool selected() const { return contents_->visible(); } |
| 44 void SetSelected(bool selected); |
| 45 |
| 46 // Overridden from View: |
| 47 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE; |
| 48 virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE; |
| 49 virtual void OnMouseCaptureLost() OVERRIDE; |
| 50 virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE; |
| 51 virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE; |
| 52 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; |
| 53 virtual gfx::Size GetPreferredSize() OVERRIDE; |
| 54 virtual void Layout() OVERRIDE; |
| 55 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; |
| 56 |
| 57 private: |
| 58 void SetTitleColor(SkColor color); |
| 59 void PaintTabBorder(gfx::Canvas* canvas); |
| 60 |
| 61 TabbedPane* tabbed_pane_; |
| 62 Label* title_; |
| 63 // The content view associated with this tab. |
| 64 View* contents_; |
| 65 |
| 66 DISALLOW_COPY_AND_ASSIGN(Tab); |
| 67 }; |
| 68 |
| 69 // The tab strip shown above the tab contents. |
| 70 class TabStrip : public View { |
| 71 public: |
| 72 TabStrip(); |
| 73 virtual ~TabStrip(); |
| 74 |
| 75 // Overridden from View: |
| 76 virtual gfx::Size GetPreferredSize() OVERRIDE; |
| 77 virtual void Layout() OVERRIDE; |
| 78 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; |
| 79 |
| 80 private: |
| 81 DISALLOW_COPY_AND_ASSIGN(TabStrip); |
| 82 }; |
| 83 |
| 84 Tab::Tab(TabbedPane* tabbed_pane, const string16& title, View* contents) |
| 85 : tabbed_pane_(tabbed_pane), |
| 86 title_(new Label(title, gfx::Font().DeriveFont(0, gfx::Font::BOLD))), |
| 87 contents_(contents) { |
| 88 SetTitleColor(kTabTitleColor_Inactive); |
| 89 AddChildView(title_); |
| 90 } |
| 91 |
| 92 Tab::~Tab() {} |
| 93 |
| 94 void Tab::SetSelected(bool selected) { |
| 95 contents_->SetVisible(selected); |
| 96 SetTitleColor(selected ? kTabTitleColor_Active : kTabTitleColor_Inactive); |
| 97 } |
| 98 |
| 99 bool Tab::OnMousePressed(const ui::MouseEvent& event) { |
| 100 SetTitleColor(kTabTitleColor_Pressed); |
| 101 return true; |
| 102 } |
| 103 |
| 104 void Tab::OnMouseReleased(const ui::MouseEvent& event) { |
| 105 SetTitleColor(selected() ? kTabTitleColor_Active : kTabTitleColor_Hovered); |
| 106 if (GetLocalBounds().Contains(event.location())) |
| 107 tabbed_pane_->SelectTab(this); |
| 108 } |
| 109 |
| 110 void Tab::OnMouseCaptureLost() { |
| 111 SetTitleColor(kTabTitleColor_Inactive); |
| 112 } |
| 113 |
| 114 void Tab::OnMouseEntered(const ui::MouseEvent& event) { |
| 115 SetTitleColor(selected() ? kTabTitleColor_Active : kTabTitleColor_Hovered); |
| 116 } |
| 117 |
| 118 void Tab::OnMouseExited(const ui::MouseEvent& event) { |
| 119 SetTitleColor(selected() ? kTabTitleColor_Active : kTabTitleColor_Inactive); |
| 120 } |
| 121 |
| 122 void Tab::OnGestureEvent(ui::GestureEvent* event) { |
| 123 switch (event->type()) { |
| 124 case ui::ET_GESTURE_TAP_DOWN: |
| 125 SetTitleColor(kTabTitleColor_Pressed); |
| 126 break; |
| 127 case ui::ET_GESTURE_TAP: |
| 128 // SelectTab also sets the right tab color. |
| 129 tabbed_pane_->SelectTab(this); |
| 130 break; |
| 131 case ui::ET_GESTURE_TAP_CANCEL: |
| 132 SetTitleColor(selected() ? |
| 133 kTabTitleColor_Active : kTabTitleColor_Inactive); |
| 134 break; |
| 135 default: |
| 136 break; |
| 137 } |
| 138 event->SetHandled(); |
| 139 } |
| 140 |
| 141 gfx::Size Tab::GetPreferredSize() { |
| 142 gfx::Size size(title_->GetPreferredSize()); |
| 143 size.Enlarge(30, 10); |
| 144 const int kTabMinWidth = 54; |
| 145 if (size.width() < kTabMinWidth) |
| 146 size.set_width(kTabMinWidth); |
| 147 return size; |
| 148 } |
| 149 |
| 150 void Tab::Layout() { |
| 151 gfx::Rect bounds = GetLocalBounds(); |
| 152 bounds.ClampToCenteredSize(title_->GetPreferredSize()); |
| 153 title_->SetBoundsRect(bounds); |
| 154 } |
| 155 |
| 156 void Tab::OnPaint(gfx::Canvas* canvas) { |
| 157 if (selected()) { |
| 158 canvas->DrawColor(kTabBackgroundColor); |
| 159 PaintTabBorder(canvas); |
| 160 } |
| 161 View::OnPaint(canvas); |
| 162 } |
| 163 |
| 164 void Tab::SetTitleColor(SkColor color) { |
| 165 title_->SetEnabledColor(color); |
| 166 SchedulePaint(); |
| 167 } |
| 168 |
| 169 void Tab::PaintTabBorder(gfx::Canvas* canvas) { |
| 170 SkPath path; |
| 171 // Clip the bottom of the border by extending its rect height beyond the tab. |
| 172 const int tab_height = height() + kTabBorderThickness; |
| 173 SkRect bounds = { 0, 0, SkIntToScalar(width()), SkIntToScalar(tab_height) }; |
| 174 SkScalar radii[8] = { kTabBorderRadius, kTabBorderRadius, |
| 175 kTabBorderRadius, kTabBorderRadius, |
| 176 0, 0, |
| 177 0, 0 }; |
| 178 path.addRoundRect(bounds, radii, SkPath::kCW_Direction); |
| 179 |
| 180 SkPaint paint; |
| 181 paint.setAntiAlias(true); |
| 182 paint.setStyle(SkPaint::kStroke_Style); |
| 183 paint.setColor(kTabBorderColor); |
| 184 paint.setStrokeWidth(kTabBorderThickness * 2); |
| 185 canvas->DrawPath(path, paint); |
| 186 } |
| 187 |
| 188 TabStrip::TabStrip() { |
| 189 const int kCount = 4; |
| 190 // This gradient is designed to mach WebUI tabbed panes. |
| 191 SkColor colors[kCount] = { |
| 192 SkColorSetRGB(0xff, 0xff, 0xff), |
| 193 SkColorSetRGB(0xff, 0xff, 0xff), |
| 194 SkColorSetRGB(0xfa, 0xfa, 0xfa), |
| 195 SkColorSetRGB(0xf2, 0xf2, 0xf2) |
| 196 }; |
| 197 SkScalar pos[kCount] = { 0.0f, 0.6f, 0.8f, 1.0f }; |
| 198 set_background(Background::CreateVerticalMultiColorGradientBackground( |
| 199 colors, pos, kCount)); |
| 200 } |
| 201 |
| 202 TabStrip::~TabStrip() {} |
| 203 |
| 204 gfx::Size TabStrip::GetPreferredSize() { |
| 205 gfx::Size size; |
| 206 for (int i = 0; i < child_count(); ++i) { |
| 207 const gfx::Size child_size = child_at(i)->GetPreferredSize(); |
| 208 size.SetSize(size.width() + child_size.width(), |
| 209 std::max(size.height(), child_size.height())); |
| 210 } |
| 211 return size; |
| 212 } |
| 213 |
| 214 void TabStrip::Layout() { |
| 215 const int kTabOffset = 18; |
| 216 int x = kTabOffset; // Layout tabs with an offset to the tabstrip border. |
| 217 for (int i = 0; i < child_count(); ++i) { |
| 218 gfx::Size ps = child_at(i)->GetPreferredSize(); |
| 219 child_at(i)->SetBounds(x, 0, ps.width(), ps.height()); |
| 220 x = child_at(i)->bounds().right(); |
| 221 } |
| 222 } |
| 223 |
| 224 void TabStrip::OnPaint(gfx::Canvas* canvas) { |
| 225 OnPaintBackground(canvas); |
| 226 |
| 227 // Draw the TabStrip border. |
| 228 SkPaint paint; |
| 229 paint.setColor(kTabBorderColor); |
| 230 paint.setStrokeWidth(kTabBorderThickness); |
| 231 SkScalar line_y = SkIntToScalar(height()) - kTabBorderThickness; |
| 232 SkScalar line_width = SkIntToScalar(width()); |
| 233 canvas->sk_canvas()->drawLine(0, line_y, line_width, line_y, paint); |
| 234 } |
39 | 235 |
40 TabbedPane::TabbedPane() | 236 TabbedPane::TabbedPane() |
41 : native_tabbed_pane_(NULL), | 237 : listener_(NULL), |
42 #if defined(OS_WIN) && !defined(USE_AURA) | 238 tab_strip_(new TabStrip()), |
43 use_native_win_control_(false), | 239 contents_(new View()), |
44 #endif | 240 selected_tab_index_(-1) { |
45 listener_(NULL) { | |
46 set_focusable(true); | 241 set_focusable(true); |
47 } | 242 AddChildView(tab_strip_); |
48 | 243 AddChildView(contents_); |
49 TabbedPane::~TabbedPane() { | 244 } |
50 } | 245 |
| 246 TabbedPane::~TabbedPane() {} |
51 | 247 |
52 int TabbedPane::GetTabCount() { | 248 int TabbedPane::GetTabCount() { |
53 return native_tabbed_pane_->GetTabCount(); | 249 DCHECK_EQ(tab_strip_->child_count(), contents_->child_count()); |
54 } | 250 return contents_->child_count(); |
55 | |
56 int TabbedPane::GetSelectedTabIndex() { | |
57 return native_tabbed_pane_->GetSelectedTabIndex(); | |
58 } | 251 } |
59 | 252 |
60 View* TabbedPane::GetSelectedTab() { | 253 View* TabbedPane::GetSelectedTab() { |
61 return native_tabbed_pane_->GetSelectedTab(); | 254 return selected_tab_index() < 0 ? |
| 255 NULL : GetTabAt(selected_tab_index())->contents(); |
62 } | 256 } |
63 | 257 |
64 void TabbedPane::AddTab(const string16& title, View* contents) { | 258 void TabbedPane::AddTab(const string16& title, View* contents) { |
65 native_tabbed_pane_->AddTab(title, contents); | 259 AddTabAtIndex(tab_strip_->child_count(), title, contents); |
66 PreferredSizeChanged(); | |
67 } | 260 } |
68 | 261 |
69 void TabbedPane::AddTabAtIndex(int index, | 262 void TabbedPane::AddTabAtIndex(int index, |
70 const string16& title, | 263 const string16& title, |
71 View* contents, | 264 View* contents) { |
72 bool select_if_first_tab) { | 265 DCHECK(index >= 0 && index <= GetTabCount()); |
73 native_tabbed_pane_->AddTabAtIndex(index, title, contents, | 266 contents->SetVisible(false); |
74 select_if_first_tab); | 267 |
| 268 tab_strip_->AddChildViewAt(new Tab(this, title, contents), index); |
| 269 contents_->AddChildViewAt(contents, index); |
| 270 if (selected_tab_index() < 0) |
| 271 SelectTabAt(index); |
| 272 |
75 PreferredSizeChanged(); | 273 PreferredSizeChanged(); |
76 } | 274 } |
77 | 275 |
78 View* TabbedPane::RemoveTabAtIndex(int index) { | |
79 View* tab = native_tabbed_pane_->RemoveTabAtIndex(index); | |
80 PreferredSizeChanged(); | |
81 return tab; | |
82 } | |
83 | |
84 void TabbedPane::SelectTabAt(int index) { | 276 void TabbedPane::SelectTabAt(int index) { |
85 native_tabbed_pane_->SelectTabAt(index); | 277 DCHECK(index >= 0 && index < GetTabCount()); |
86 } | 278 if (index == selected_tab_index()) |
87 | 279 return; |
88 void TabbedPane::SetAccessibleName(const string16& name) { | 280 |
89 accessible_name_ = name; | 281 if (selected_tab_index() >= 0) |
| 282 GetTabAt(selected_tab_index())->SetSelected(false); |
| 283 |
| 284 selected_tab_index_ = index; |
| 285 Tab* tab = GetTabAt(index); |
| 286 tab->SetSelected(true); |
| 287 tab_strip_->SchedulePaint(); |
| 288 |
| 289 FocusManager* focus_manager = tab->contents()->GetFocusManager(); |
| 290 if (focus_manager) { |
| 291 const View* focused_view = focus_manager->GetFocusedView(); |
| 292 if (focused_view && contents_->Contains(focused_view) && |
| 293 !tab->contents()->Contains(focused_view)) |
| 294 focus_manager->SetFocusedView(tab->contents()); |
| 295 } |
| 296 |
| 297 if (listener()) |
| 298 listener()->TabSelectedAt(index); |
| 299 } |
| 300 |
| 301 void TabbedPane::SelectTab(Tab* tab) { |
| 302 const int index = tab_strip_->GetIndexOf(tab); |
| 303 if (index >= 0) |
| 304 SelectTabAt(index); |
90 } | 305 } |
91 | 306 |
92 gfx::Size TabbedPane::GetPreferredSize() { | 307 gfx::Size TabbedPane::GetPreferredSize() { |
93 return native_tabbed_pane_ ? | 308 gfx::Size size; |
94 native_tabbed_pane_->GetPreferredSize() : gfx::Size(); | 309 for (int i = 0; i < contents_->child_count(); ++i) |
95 } | 310 size.ClampToMin(contents_->child_at(i)->GetPreferredSize()); |
96 | 311 size.Enlarge(0, tab_strip_->GetPreferredSize().height()); |
97 void TabbedPane::LoadAccelerators() { | 312 return size; |
98 // Ctrl+Shift+Tab | 313 } |
99 AddAccelerator(ui::Accelerator(ui::VKEY_TAB, | 314 |
100 ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN)); | 315 Tab* TabbedPane::GetTabAt(int index) { |
101 // Ctrl+Tab | 316 return static_cast<Tab*>(tab_strip_->child_at(index)); |
102 AddAccelerator(ui::Accelerator(ui::VKEY_TAB, ui::EF_CONTROL_DOWN)); | |
103 } | 317 } |
104 | 318 |
105 void TabbedPane::Layout() { | 319 void TabbedPane::Layout() { |
106 if (native_tabbed_pane_) | 320 const gfx::Size size = tab_strip_->GetPreferredSize(); |
107 native_tabbed_pane_->GetView()->SetBounds(0, 0, width(), height()); | 321 tab_strip_->SetBounds(0, 0, width(), size.height()); |
| 322 contents_->SetBounds(0, tab_strip_->bounds().bottom(), width(), |
| 323 std::max(0, height() - size.height())); |
| 324 for (int i = 0; i < contents_->child_count(); ++i) |
| 325 contents_->child_at(i)->SetSize(contents_->size()); |
108 } | 326 } |
109 | 327 |
110 void TabbedPane::ViewHierarchyChanged(bool is_add, View* parent, View* child) { | 328 void TabbedPane::ViewHierarchyChanged(bool is_add, View* parent, View* child) { |
111 if (is_add && !native_tabbed_pane_) { | 329 if (is_add) { |
112 // The native wrapper's lifetime will be managed by the view hierarchy after | 330 // Support navigating tabs by Ctrl+Tab and Ctrl+Shift+Tab. |
113 // we call AddChildView. | 331 AddAccelerator(ui::Accelerator(ui::VKEY_TAB, |
114 native_tabbed_pane_ = CreateNativeWrapper(this); | 332 ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN)); |
115 AddChildView(native_tabbed_pane_->GetView()); | 333 AddAccelerator(ui::Accelerator(ui::VKEY_TAB, ui::EF_CONTROL_DOWN)); |
116 LoadAccelerators(); | |
117 } | 334 } |
118 } | 335 } |
119 | 336 |
120 bool TabbedPane::AcceleratorPressed(const ui::Accelerator& accelerator) { | 337 bool TabbedPane::AcceleratorPressed(const ui::Accelerator& accelerator) { |
121 // We only accept Ctrl+Tab keyboard events. | 338 // Handle Ctrl+Tab and Ctrl+Shift+Tab navigation of pages. |
122 DCHECK(accelerator.key_code() == ui::VKEY_TAB && accelerator.IsCtrlDown()); | 339 DCHECK(accelerator.key_code() == ui::VKEY_TAB && accelerator.IsCtrlDown()); |
123 | 340 const int tab_count = GetTabCount(); |
124 int tab_count = GetTabCount(); | |
125 if (tab_count <= 1) | 341 if (tab_count <= 1) |
126 return false; | 342 return false; |
127 int selected_tab_index = GetSelectedTabIndex(); | 343 const int increment = accelerator.IsShiftDown() ? -1 : 1; |
128 int next_tab_index = accelerator.IsShiftDown() ? | 344 int next_tab_index = (selected_tab_index() + increment) % tab_count; |
129 (selected_tab_index - 1) % tab_count : | |
130 (selected_tab_index + 1) % tab_count; | |
131 // Wrap around. | 345 // Wrap around. |
132 if (next_tab_index < 0) | 346 if (next_tab_index < 0) |
133 next_tab_index += tab_count; | 347 next_tab_index += tab_count; |
134 SelectTabAt(next_tab_index); | 348 SelectTabAt(next_tab_index); |
135 return true; | 349 return true; |
136 } | 350 } |
137 | 351 |
138 std::string TabbedPane::GetClassName() const { | 352 std::string TabbedPane::GetClassName() const { |
139 return kViewClassName; | 353 return kViewClassName; |
140 } | 354 } |
141 | 355 |
142 void TabbedPane::OnFocus() { | 356 void TabbedPane::OnFocus() { |
143 // Forward the focus to the wrapper. | 357 View::OnFocus(); |
144 if (native_tabbed_pane_) { | |
145 native_tabbed_pane_->SetFocus(); | |
146 | 358 |
147 View* selected_tab = GetSelectedTab(); | 359 View* selected_tab = GetSelectedTab(); |
148 if (selected_tab) { | 360 if (selected_tab) { |
149 selected_tab->GetWidget()->NotifyAccessibilityEvent( | 361 selected_tab->GetWidget()->NotifyAccessibilityEvent( |
150 selected_tab, ui::AccessibilityTypes::EVENT_FOCUS, true); | 362 selected_tab, ui::AccessibilityTypes::EVENT_FOCUS, true); |
151 } | |
152 } else { | |
153 View::OnFocus(); // Will focus the RootView window (so we still get | |
154 // keyboard messages). | |
155 } | 363 } |
156 } | 364 } |
157 | 365 |
158 void TabbedPane::OnPaintFocusBorder(gfx::Canvas* canvas) { | |
159 if (NativeViewHost::kRenderNativeControlFocus) | |
160 View::OnPaintFocusBorder(canvas); | |
161 } | |
162 | |
163 void TabbedPane::GetAccessibleState(ui::AccessibleViewState* state) { | 366 void TabbedPane::GetAccessibleState(ui::AccessibleViewState* state) { |
164 state->role = ui::AccessibilityTypes::ROLE_PAGETABLIST; | 367 state->role = ui::AccessibilityTypes::ROLE_PAGETABLIST; |
165 state->name = accessible_name_; | |
166 } | 368 } |
167 | 369 |
168 } // namespace views | 370 } // namespace views |
OLD | NEW |