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/gfx/canvas.h" | 10 #include "ui/views/controls/native/native_view_host.h" |
11 #include "ui/gfx/font.h" | 11 #include "ui/views/controls/tabbed_pane/native_tabbed_pane_views.h" |
12 #include "ui/views/controls/label.h" | 12 #include "ui/views/controls/tabbed_pane/native_tabbed_pane_wrapper.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" | |
15 #include "ui/views/widget/widget.h" | 14 #include "ui/views/widget/widget.h" |
16 | 15 |
| 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 |
17 namespace { | 22 namespace { |
18 | 23 |
19 const char kViewClassName[] = "views/TabbedPane"; | 24 views::NativeTabbedPaneWrapper* CreateNativeWrapper( |
20 | 25 views::TabbedPane* tabbed_pane) { |
21 // TODO(markusheintz|msw): Use NativeTheme colors. | 26 #if defined(OS_WIN) && !defined(USE_AURA) |
22 const SkColor kTabTitleColor_Inactive = SkColorSetRGB(0x66, 0x66, 0x66); | 27 if (tabbed_pane->use_native_win_control()) |
23 const SkColor kTabTitleColor_Active = SkColorSetRGB(0x20, 0x20, 0x20); | 28 return new views::NativeTabbedPaneWin(tabbed_pane); |
24 const SkColor kTabTitleColor_Pressed = SkColorSetRGB(0x33, 0x33, 0x33); | 29 #endif |
25 const SkColor kTabTitleColor_Hovered = SkColorSetRGB(0x22, 0x22, 0x22); | 30 return new views::NativeTabbedPaneViews(tabbed_pane); |
26 const SkColor kTabBackgroundColor = SK_ColorWHITE; | 31 } |
27 const SkColor kTabBorderColor = SkColorSetRGB(0xCC, 0xCC, 0xCC); | |
28 const SkScalar kTabBorderThickness = 1.0f; | |
29 const SkScalar kTabBorderRadius = 2.0f; | |
30 | 32 |
31 } // namespace | 33 } // namespace |
32 | 34 |
33 namespace views { | 35 namespace views { |
34 | 36 |
35 // The tab view shown in the tab strip. | 37 // static |
36 class Tab : public View { | 38 const char TabbedPane::kViewClassName[] = "views/TabbedPane"; |
37 public: | |
38 Tab(TabbedPane* tabbed_pane, const string16& title, View* contents); | |
39 virtual ~Tab(); | |
40 | 39 |
41 View* contents() const { return contents_; } | 40 TabbedPane::TabbedPane() |
42 | 41 : native_tabbed_pane_(NULL), |
43 bool selected() const { return contents_->visible(); } | 42 #if defined(OS_WIN) && !defined(USE_AURA) |
44 void SetSelected(bool selected); | 43 use_native_win_control_(false), |
45 | 44 #endif |
46 // Overridden from View: | 45 listener_(NULL) { |
47 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE; | 46 set_focusable(true); |
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 } | 47 } |
91 | 48 |
92 Tab::~Tab() {} | 49 TabbedPane::~TabbedPane() { |
93 | |
94 void Tab::SetSelected(bool selected) { | |
95 contents_->SetVisible(selected); | |
96 SetTitleColor(selected ? kTabTitleColor_Active : kTabTitleColor_Inactive); | |
97 } | 50 } |
98 | 51 |
99 bool Tab::OnMousePressed(const ui::MouseEvent& event) { | 52 int TabbedPane::GetTabCount() { |
100 SetTitleColor(kTabTitleColor_Pressed); | 53 return native_tabbed_pane_->GetTabCount(); |
101 return true; | |
102 } | 54 } |
103 | 55 |
104 void Tab::OnMouseReleased(const ui::MouseEvent& event) { | 56 int TabbedPane::GetSelectedTabIndex() { |
105 SetTitleColor(selected() ? kTabTitleColor_Active : kTabTitleColor_Hovered); | 57 return native_tabbed_pane_->GetSelectedTabIndex(); |
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 } | |
235 | |
236 TabbedPane::TabbedPane() | |
237 : listener_(NULL), | |
238 tab_strip_(new TabStrip()), | |
239 contents_(new View()), | |
240 selected_tab_index_(-1) { | |
241 set_focusable(true); | |
242 AddChildView(tab_strip_); | |
243 AddChildView(contents_); | |
244 } | |
245 | |
246 TabbedPane::~TabbedPane() {} | |
247 | |
248 int TabbedPane::GetTabCount() { | |
249 DCHECK_EQ(tab_strip_->child_count(), contents_->child_count()); | |
250 return contents_->child_count(); | |
251 } | 58 } |
252 | 59 |
253 View* TabbedPane::GetSelectedTab() { | 60 View* TabbedPane::GetSelectedTab() { |
254 return selected_tab_index() < 0 ? | 61 return native_tabbed_pane_->GetSelectedTab(); |
255 NULL : GetTabAt(selected_tab_index())->contents(); | |
256 } | 62 } |
257 | 63 |
258 void TabbedPane::AddTab(const string16& title, View* contents) { | 64 void TabbedPane::AddTab(const string16& title, View* contents) { |
259 AddTabAtIndex(tab_strip_->child_count(), title, contents); | 65 native_tabbed_pane_->AddTab(title, contents); |
| 66 PreferredSizeChanged(); |
260 } | 67 } |
261 | 68 |
262 void TabbedPane::AddTabAtIndex(int index, | 69 void TabbedPane::AddTabAtIndex(int index, |
263 const string16& title, | 70 const string16& title, |
264 View* contents) { | 71 View* contents, |
265 DCHECK(index >= 0 && index <= GetTabCount()); | 72 bool select_if_first_tab) { |
266 contents->set_owned_by_client(); | 73 native_tabbed_pane_->AddTabAtIndex(index, title, contents, |
267 contents->SetVisible(false); | 74 select_if_first_tab); |
268 | |
269 tab_strip_->AddChildViewAt(new Tab(this, title, contents), index); | |
270 contents_->AddChildViewAt(contents, index); | |
271 if (selected_tab_index() < 0) | |
272 SelectTabAt(index); | |
273 | |
274 PreferredSizeChanged(); | 75 PreferredSizeChanged(); |
275 } | 76 } |
276 | 77 |
277 void TabbedPane::SelectTabAt(int index) { | 78 View* TabbedPane::RemoveTabAtIndex(int index) { |
278 DCHECK(index >= 0 && index < GetTabCount()); | 79 View* tab = native_tabbed_pane_->RemoveTabAtIndex(index); |
279 if (index == selected_tab_index()) | 80 PreferredSizeChanged(); |
280 return; | 81 return tab; |
281 | |
282 if (selected_tab_index() >= 0) | |
283 GetTabAt(selected_tab_index())->SetSelected(false); | |
284 | |
285 selected_tab_index_ = index; | |
286 Tab* tab = GetTabAt(index); | |
287 tab->SetSelected(true); | |
288 tab_strip_->SchedulePaint(); | |
289 | |
290 FocusManager* focus_manager = tab->contents()->GetFocusManager(); | |
291 if (focus_manager) { | |
292 const View* focused_view = focus_manager->GetFocusedView(); | |
293 if (focused_view && contents_->Contains(focused_view) && | |
294 !tab->contents()->Contains(focused_view)) | |
295 focus_manager->SetFocusedView(tab->contents()); | |
296 } | |
297 | |
298 if (listener()) | |
299 listener()->TabSelectedAt(index); | |
300 } | 82 } |
301 | 83 |
302 void TabbedPane::SelectTab(Tab* tab) { | 84 void TabbedPane::SelectTabAt(int index) { |
303 const int index = tab_strip_->GetIndexOf(tab); | 85 native_tabbed_pane_->SelectTabAt(index); |
304 if (index >= 0) | 86 } |
305 SelectTabAt(index); | 87 |
| 88 void TabbedPane::SetAccessibleName(const string16& name) { |
| 89 accessible_name_ = name; |
306 } | 90 } |
307 | 91 |
308 gfx::Size TabbedPane::GetPreferredSize() { | 92 gfx::Size TabbedPane::GetPreferredSize() { |
309 gfx::Size size; | 93 return native_tabbed_pane_ ? |
310 for (int i = 0; i < contents_->child_count(); ++i) | 94 native_tabbed_pane_->GetPreferredSize() : gfx::Size(); |
311 size.ClampToMin(contents_->child_at(i)->GetPreferredSize()); | |
312 size.Enlarge(0, tab_strip_->GetPreferredSize().height()); | |
313 return size; | |
314 } | 95 } |
315 | 96 |
316 Tab* TabbedPane::GetTabAt(int index) { | 97 void TabbedPane::LoadAccelerators() { |
317 return static_cast<Tab*>(tab_strip_->child_at(index)); | 98 // Ctrl+Shift+Tab |
| 99 AddAccelerator(ui::Accelerator(ui::VKEY_TAB, |
| 100 ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN)); |
| 101 // Ctrl+Tab |
| 102 AddAccelerator(ui::Accelerator(ui::VKEY_TAB, ui::EF_CONTROL_DOWN)); |
318 } | 103 } |
319 | 104 |
320 void TabbedPane::Layout() { | 105 void TabbedPane::Layout() { |
321 const gfx::Size size = tab_strip_->GetPreferredSize(); | 106 if (native_tabbed_pane_) |
322 tab_strip_->SetBounds(0, 0, width(), size.height()); | 107 native_tabbed_pane_->GetView()->SetBounds(0, 0, width(), height()); |
323 contents_->SetBounds(0, tab_strip_->bounds().bottom(), width(), | |
324 std::max(0, height() - size.height())); | |
325 for (int i = 0; i < contents_->child_count(); ++i) | |
326 contents_->child_at(i)->SetSize(contents_->size()); | |
327 } | 108 } |
328 | 109 |
329 void TabbedPane::ViewHierarchyChanged(bool is_add, View* parent, View* child) { | 110 void TabbedPane::ViewHierarchyChanged(bool is_add, View* parent, View* child) { |
330 if (is_add) { | 111 if (is_add && !native_tabbed_pane_) { |
331 // Support navigating tabs by Ctrl+Tab and Ctrl+Shift+Tab. | 112 // The native wrapper's lifetime will be managed by the view hierarchy after |
332 AddAccelerator(ui::Accelerator(ui::VKEY_TAB, | 113 // we call AddChildView. |
333 ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN)); | 114 native_tabbed_pane_ = CreateNativeWrapper(this); |
334 AddAccelerator(ui::Accelerator(ui::VKEY_TAB, ui::EF_CONTROL_DOWN)); | 115 AddChildView(native_tabbed_pane_->GetView()); |
| 116 LoadAccelerators(); |
335 } | 117 } |
336 } | 118 } |
337 | 119 |
338 bool TabbedPane::AcceleratorPressed(const ui::Accelerator& accelerator) { | 120 bool TabbedPane::AcceleratorPressed(const ui::Accelerator& accelerator) { |
339 // Handle Ctrl+Tab and Ctrl+Shift+Tab navigation of pages. | 121 // We only accept Ctrl+Tab keyboard events. |
340 DCHECK(accelerator.key_code() == ui::VKEY_TAB && accelerator.IsCtrlDown()); | 122 DCHECK(accelerator.key_code() == ui::VKEY_TAB && accelerator.IsCtrlDown()); |
341 const int tab_count = GetTabCount(); | 123 |
| 124 int tab_count = GetTabCount(); |
342 if (tab_count <= 1) | 125 if (tab_count <= 1) |
343 return false; | 126 return false; |
344 const int increment = accelerator.IsShiftDown() ? -1 : 1; | 127 int selected_tab_index = GetSelectedTabIndex(); |
345 int next_tab_index = (selected_tab_index() + increment) % tab_count; | 128 int next_tab_index = accelerator.IsShiftDown() ? |
| 129 (selected_tab_index - 1) % tab_count : |
| 130 (selected_tab_index + 1) % tab_count; |
346 // Wrap around. | 131 // Wrap around. |
347 if (next_tab_index < 0) | 132 if (next_tab_index < 0) |
348 next_tab_index += tab_count; | 133 next_tab_index += tab_count; |
349 SelectTabAt(next_tab_index); | 134 SelectTabAt(next_tab_index); |
350 return true; | 135 return true; |
351 } | 136 } |
352 | 137 |
353 std::string TabbedPane::GetClassName() const { | 138 std::string TabbedPane::GetClassName() const { |
354 return kViewClassName; | 139 return kViewClassName; |
355 } | 140 } |
356 | 141 |
357 void TabbedPane::OnFocus() { | 142 void TabbedPane::OnFocus() { |
358 View::OnFocus(); | 143 // Forward the focus to the wrapper. |
| 144 if (native_tabbed_pane_) { |
| 145 native_tabbed_pane_->SetFocus(); |
359 | 146 |
360 View* selected_tab = GetSelectedTab(); | 147 View* selected_tab = GetSelectedTab(); |
361 if (selected_tab) { | 148 if (selected_tab) { |
362 selected_tab->GetWidget()->NotifyAccessibilityEvent( | 149 selected_tab->GetWidget()->NotifyAccessibilityEvent( |
363 selected_tab, ui::AccessibilityTypes::EVENT_FOCUS, true); | 150 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). |
364 } | 155 } |
365 } | 156 } |
366 | 157 |
| 158 void TabbedPane::OnPaintFocusBorder(gfx::Canvas* canvas) { |
| 159 if (NativeViewHost::kRenderNativeControlFocus) |
| 160 View::OnPaintFocusBorder(canvas); |
| 161 } |
| 162 |
367 void TabbedPane::GetAccessibleState(ui::AccessibleViewState* state) { | 163 void TabbedPane::GetAccessibleState(ui::AccessibleViewState* state) { |
368 state->role = ui::AccessibilityTypes::ROLE_PAGETABLIST; | 164 state->role = ui::AccessibilityTypes::ROLE_PAGETABLIST; |
| 165 state->name = accessible_name_; |
369 } | 166 } |
370 | 167 |
371 } // namespace views | 168 } // namespace views |
OLD | NEW |