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(contents_); | |
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(contents_); | |
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 tabs_(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(tabs_); |
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(), tabs_->child_count()); |
54 } | 250 return tabs_->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->set_owned_by_client(); |
74 select_if_first_tab); | 267 contents->SetVisible(false); |
268 | |
269 tab_strip_->AddChildViewAt(new Tab(this, title, contents), index); | |
270 tabs_->AddChildViewAt(contents, index); | |
271 if (selected_tab_index() < 0) | |
272 SelectTabAt(index); | |
273 | |
75 PreferredSizeChanged(); | 274 PreferredSizeChanged(); |
76 } | 275 } |
77 | 276 |
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) { | 277 void TabbedPane::SelectTabAt(int index) { |
85 native_tabbed_pane_->SelectTabAt(index); | 278 DCHECK(index >= 0 && index < GetTabCount()); |
86 } | 279 if (index == selected_tab_index()) |
87 | 280 return; |
88 void TabbedPane::SetAccessibleName(const string16& name) { | 281 |
89 accessible_name_ = name; | 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->contents()->SetVisible(true); | |
sky
2013/02/11 16:26:36
How does the previously selected tab get hidden?
msw
2013/02/11 20:41:43
Tab::SetSelected(false) at line 283 hides the old
| |
289 tab_strip_->SchedulePaint(); | |
290 | |
291 FocusManager* focus_manager = tab->contents()->GetFocusManager(); | |
292 if (focus_manager) { | |
293 const View* focused_view = focus_manager->GetFocusedView(); | |
294 if (focused_view && tabs_->Contains(focused_view) && | |
295 !tab->contents()->Contains(focused_view)) | |
296 focus_manager->SetFocusedView(tab->contents()); | |
297 } | |
298 | |
299 if (listener()) | |
300 listener()->TabSelectedAt(index); | |
301 } | |
302 | |
303 void TabbedPane::SelectTab(View* tab) { | |
304 const int index = tabs_->GetIndexOf(tab); | |
305 if (index >= 0) | |
306 SelectTabAt(index); | |
90 } | 307 } |
91 | 308 |
92 gfx::Size TabbedPane::GetPreferredSize() { | 309 gfx::Size TabbedPane::GetPreferredSize() { |
93 return native_tabbed_pane_ ? | 310 gfx::Size size; |
94 native_tabbed_pane_->GetPreferredSize() : gfx::Size(); | 311 for (int i = 0; i < tabs_->child_count(); ++i) |
95 } | 312 size.ClampToMin(tabs_->child_at(i)->GetPreferredSize()); |
96 | 313 size.Enlarge(0, tab_strip_->GetPreferredSize().height()); |
97 void TabbedPane::LoadAccelerators() { | 314 return size; |
98 // Ctrl+Shift+Tab | 315 } |
99 AddAccelerator(ui::Accelerator(ui::VKEY_TAB, | 316 |
100 ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN)); | 317 Tab* TabbedPane::GetTabAt(int index) { |
101 // Ctrl+Tab | 318 return static_cast<Tab*>(tab_strip_->child_at(index)); |
102 AddAccelerator(ui::Accelerator(ui::VKEY_TAB, ui::EF_CONTROL_DOWN)); | |
103 } | 319 } |
104 | 320 |
105 void TabbedPane::Layout() { | 321 void TabbedPane::Layout() { |
106 if (native_tabbed_pane_) | 322 const gfx::Size size = tab_strip_->GetPreferredSize(); |
107 native_tabbed_pane_->GetView()->SetBounds(0, 0, width(), height()); | 323 tab_strip_->SetBounds(0, 0, width(), size.height()); |
324 tabs_->SetBounds(0, tab_strip_->bounds().bottom(), width(), | |
325 std::max(0, height() - size.height())); | |
326 for (int i = 0; i < tabs_->child_count(); ++i) | |
327 tabs_->child_at(i)->SetSize(tabs_->size()); | |
108 } | 328 } |
109 | 329 |
110 void TabbedPane::ViewHierarchyChanged(bool is_add, View* parent, View* child) { | 330 void TabbedPane::ViewHierarchyChanged(bool is_add, View* parent, View* child) { |
111 if (is_add && !native_tabbed_pane_) { | 331 if (is_add) { |
112 // The native wrapper's lifetime will be managed by the view hierarchy after | 332 // Support navigating tabs by Ctrl+Tab and Ctrl+Shift+Tab. |
113 // we call AddChildView. | 333 AddAccelerator(ui::Accelerator(ui::VKEY_TAB, |
114 native_tabbed_pane_ = CreateNativeWrapper(this); | 334 ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN)); |
115 AddChildView(native_tabbed_pane_->GetView()); | 335 AddAccelerator(ui::Accelerator(ui::VKEY_TAB, ui::EF_CONTROL_DOWN)); |
116 LoadAccelerators(); | |
117 } | 336 } |
118 } | 337 } |
119 | 338 |
120 bool TabbedPane::AcceleratorPressed(const ui::Accelerator& accelerator) { | 339 bool TabbedPane::AcceleratorPressed(const ui::Accelerator& accelerator) { |
121 // We only accept Ctrl+Tab keyboard events. | |
122 DCHECK(accelerator.key_code() == ui::VKEY_TAB && accelerator.IsCtrlDown()); | 340 DCHECK(accelerator.key_code() == ui::VKEY_TAB && accelerator.IsCtrlDown()); |
123 | 341 const int tab_count = GetTabCount(); |
124 int tab_count = GetTabCount(); | |
125 if (tab_count <= 1) | 342 if (tab_count <= 1) |
126 return false; | 343 return false; |
127 int selected_tab_index = GetSelectedTabIndex(); | 344 const int increment = accelerator.IsShiftDown() ? -1 : 1; |
128 int next_tab_index = accelerator.IsShiftDown() ? | 345 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. | 346 // Wrap around. |
132 if (next_tab_index < 0) | 347 if (next_tab_index < 0) |
133 next_tab_index += tab_count; | 348 next_tab_index += tab_count; |
134 SelectTabAt(next_tab_index); | 349 SelectTabAt(next_tab_index); |
135 return true; | 350 return true; |
136 } | 351 } |
137 | 352 |
138 std::string TabbedPane::GetClassName() const { | 353 std::string TabbedPane::GetClassName() const { |
139 return kViewClassName; | 354 return kViewClassName; |
140 } | 355 } |
141 | 356 |
142 void TabbedPane::OnFocus() { | 357 void TabbedPane::OnFocus() { |
143 // Forward the focus to the wrapper. | 358 View::OnFocus(); |
144 if (native_tabbed_pane_) { | |
145 native_tabbed_pane_->SetFocus(); | |
146 | 359 |
147 View* selected_tab = GetSelectedTab(); | 360 View* selected_tab = GetSelectedTab(); |
148 if (selected_tab) { | 361 if (selected_tab) { |
149 selected_tab->GetWidget()->NotifyAccessibilityEvent( | 362 selected_tab->GetWidget()->NotifyAccessibilityEvent( |
150 selected_tab, ui::AccessibilityTypes::EVENT_FOCUS, true); | 363 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 } | 364 } |
156 } | 365 } |
157 | 366 |
158 void TabbedPane::OnPaintFocusBorder(gfx::Canvas* canvas) { | |
159 if (NativeViewHost::kRenderNativeControlFocus) | |
160 View::OnPaintFocusBorder(canvas); | |
161 } | |
162 | |
163 void TabbedPane::GetAccessibleState(ui::AccessibleViewState* state) { | 367 void TabbedPane::GetAccessibleState(ui::AccessibleViewState* state) { |
164 state->role = ui::AccessibilityTypes::ROLE_PAGETABLIST; | 368 state->role = ui::AccessibilityTypes::ROLE_PAGETABLIST; |
165 state->name = accessible_name_; | |
166 } | 369 } |
167 | 370 |
168 } // namespace views | 371 } // namespace views |
OLD | NEW |