OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "ui/views/controls/tabbed_pane/native_tabbed_pane_views.h" | |
6 | |
7 #include "base/logging.h" | |
8 #include "base/stl_util.h" | |
9 #include "ui/base/resource/resource_bundle.h" | |
10 #include "ui/gfx/canvas.h" | |
11 #include "ui/gfx/font.h" | |
12 #include "ui/views/controls/tabbed_pane/tabbed_pane.h" | |
13 #include "ui/views/controls/tabbed_pane/tabbed_pane_listener.h" | |
14 #include "ui/views/layout/fill_layout.h" | |
15 #include "ui/views/widget/widget.h" | |
16 | |
17 namespace views { | |
18 | |
19 const SkColor kTabTitleColor_Inactive = SkColorSetRGB(0x66, 0x66, 0x66); | |
20 const SkColor kTabTitleColor_Active = SkColorSetRGB(0x20, 0x20, 0x20); | |
21 const SkColor kTabTitleColor_Pressed = SkColorSetRGB(0x33, 0x33, 0x33); | |
22 const SkColor kTabTitleColor_Hovered = SkColorSetRGB(0x22, 0x22, 0x22); | |
23 // TODO(markusheintz): The tab background color should be provided by the | |
24 // NativeTheme. | |
25 const SkColor kTabBackgroundColor_Active = SK_ColorWHITE; | |
26 const SkColor kTabBorderColor = SkColorSetRGB(0xCC, 0xCC, 0xCC); | |
27 const SkScalar kTabBorderThickness = 1.0f; | |
28 const SkScalar kTabBorderRadius = 2.0f; | |
29 | |
30 class TabStrip; | |
31 | |
32 class Tab : public View { | |
33 public: | |
34 Tab(TabStrip* tab_strip, const string16& title, View* contents) | |
35 : tab_strip_(tab_strip), | |
36 title_(title), | |
37 contents_(contents), | |
38 title_color_(kTabTitleColor_Inactive) {} | |
39 virtual ~Tab() {} | |
40 | |
41 static int GetMinimumTabHeight() { | |
42 return gfx::Font().GetHeight() + 10; | |
43 } | |
44 | |
45 static View* GetContents(View* tab) { | |
46 return static_cast<Tab*>(tab)->contents_; | |
47 } | |
48 | |
49 void OnSelectedStateChanged(bool selected); | |
50 | |
51 // Overridden from View: | |
52 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; | |
53 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE; | |
54 virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE; | |
55 virtual void OnMouseCaptureLost() OVERRIDE; | |
56 virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE; | |
57 virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE; | |
58 virtual gfx::Size GetPreferredSize() OVERRIDE { | |
59 const int kTabMinWidth = 54; | |
60 gfx::Size ps(GetTabTitleFont().GetStringWidth(title_), | |
61 GetMinimumTabHeight()); | |
62 ps.Enlarge(30, 10); | |
63 if (ps.width() < kTabMinWidth) | |
64 ps.set_width(kTabMinWidth); | |
65 return ps; | |
66 } | |
67 | |
68 // Overridden from ui::EventHandler. | |
69 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; | |
70 | |
71 private: | |
72 void PaintTabBackground(gfx::Canvas* canvas) { | |
73 // Fill the background. Note that we don't constrain to the bounds as | |
74 // canvas is already clipped for us. | |
75 canvas->DrawColor(kTabBackgroundColor_Active); | |
76 } | |
77 | |
78 void PaintTabBorder(gfx::Canvas* canvas) { | |
79 SkPath path; | |
80 SkRect bounds = { 0, 0, SkIntToScalar(width()), SkIntToScalar(height()) }; | |
81 SkScalar radii[8] = { kTabBorderRadius, kTabBorderRadius, | |
82 kTabBorderRadius, kTabBorderRadius, | |
83 0, 0, | |
84 0, 0 }; | |
85 path.addRoundRect(bounds, radii, SkPath::kCW_Direction); | |
86 | |
87 SkPaint paint; | |
88 paint.setAntiAlias(true); | |
89 paint.setStyle(SkPaint::kStroke_Style); | |
90 paint.setColor(kTabBorderColor); | |
91 paint.setStrokeWidth(kTabBorderThickness * 2); | |
92 | |
93 canvas->DrawPath(path, paint); | |
94 } | |
95 | |
96 void PaintTabTitle(gfx::Canvas* canvas, bool selected) { | |
97 int text_width = GetTabTitleFont().GetStringWidth(title_); | |
98 int text_height = GetTabTitleFont().GetHeight(); | |
99 int text_x = (width() - text_width) / 2; | |
100 int text_y = 5; | |
101 gfx::Rect text_rect(text_x, text_y, text_width, text_height); | |
102 canvas->DrawStringInt(title_, GetTabTitleFont(), title_color_, text_rect); | |
103 } | |
104 | |
105 void SetTitleColor(SkColor color) { | |
106 title_color_ = color; | |
107 SchedulePaint(); | |
108 } | |
109 | |
110 const gfx::Font& GetTabTitleFont() { | |
111 static gfx::Font* title_font = NULL; | |
112 if (!title_font) | |
113 title_font = new gfx::Font(gfx::Font().DeriveFont(0, gfx::Font::BOLD)); | |
114 return *title_font; | |
115 } | |
116 | |
117 TabStrip* tab_strip_; | |
118 string16 title_; | |
119 View* contents_; | |
120 SkColor title_color_; | |
121 | |
122 DISALLOW_COPY_AND_ASSIGN(Tab); | |
123 }; | |
124 | |
125 class TabStrip : public View { | |
126 public: | |
127 explicit TabStrip(NativeTabbedPaneViews* owner) | |
128 : owner_(owner), | |
129 selected_tab_(NULL) { | |
130 const int kCount = 4; | |
131 // The gradient colors are derived from the tabbed panes used for the | |
132 // WebUI. | |
133 SkColor colors[] = { | |
134 SkColorSetRGB(0xff, 0xff, 0xff), | |
135 SkColorSetRGB(0xff, 0xff, 0xff), | |
136 SkColorSetRGB(0xfa, 0xfa, 0xfa), | |
137 SkColorSetRGB(0xf2, 0xf2, 0xf2) | |
138 }; | |
139 // The relative positions of the gradient colors are derived from the | |
140 // tabbed panes used for the WebUI. | |
141 SkScalar pos[4] = {0.0f, 0.6f, 0.8f, 1.0f}; | |
142 set_background(Background::CreateVerticalMultiColorGradientBackground( | |
143 colors, pos, kCount)); | |
144 } | |
145 virtual ~TabStrip() {} | |
146 | |
147 void SelectTab(View* tab) { | |
148 if (tab == selected_tab_) | |
149 return; | |
150 if (selected_tab_) | |
151 static_cast<Tab*>(selected_tab_)->OnSelectedStateChanged(false); | |
152 selected_tab_ = tab; | |
153 static_cast<Tab*>(selected_tab_)->OnSelectedStateChanged(true); | |
154 owner_->TabSelectionChanged(tab); | |
155 } | |
156 | |
157 bool IsTabSelected(const View* tab) const { | |
158 return tab == selected_tab_; | |
159 } | |
160 | |
161 int GetSelectedIndex() const { | |
162 return GetIndexOf(selected_tab_); | |
163 } | |
164 | |
165 View* selected_tab() { return selected_tab_; } | |
166 | |
167 View* RemoveTabAt(int index) { | |
168 View* contents = Tab::GetContents(child_at(index)); | |
169 delete child_at(index); | |
170 return contents; | |
171 } | |
172 | |
173 // Overridden from View: | |
174 virtual gfx::Size GetPreferredSize() OVERRIDE { | |
175 return gfx::Size(50, Tab::GetMinimumTabHeight()); | |
176 } | |
177 virtual void Layout() OVERRIDE { | |
178 const int kTabOffset = 18; | |
179 int x = kTabOffset; // Layout tabs with an offset to the tabstrip border. | |
180 for (int i = 0; i < child_count(); ++i) { | |
181 gfx::Size ps = child_at(i)->GetPreferredSize(); | |
182 child_at(i)->SetBounds(x, 0, ps.width(), ps.height()); | |
183 x = child_at(i)->bounds().right(); | |
184 } | |
185 } | |
186 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { | |
187 OnPaintBackground(canvas); | |
188 | |
189 // Draw the TabStrip border. | |
190 SkPaint paint; | |
191 paint.setColor(kTabBorderColor); | |
192 paint.setStrokeWidth(kTabBorderThickness); | |
193 SkScalar line_y = SkIntToScalar(height()) - kTabBorderThickness; | |
194 SkScalar line_width = SkIntToScalar(width()); | |
195 canvas->sk_canvas()->drawLine(0, line_y, line_width, line_y, paint); | |
196 } | |
197 | |
198 private: | |
199 NativeTabbedPaneViews* owner_; | |
200 View* selected_tab_; | |
201 | |
202 DISALLOW_COPY_AND_ASSIGN(TabStrip); | |
203 }; | |
204 | |
205 void Tab::OnSelectedStateChanged(bool selected) { | |
206 SetTitleColor(selected ? kTabTitleColor_Active : kTabTitleColor_Inactive); | |
207 } | |
208 | |
209 void Tab::OnPaint(gfx::Canvas* canvas) { | |
210 bool selected = tab_strip_->IsTabSelected(this); | |
211 if (selected) { | |
212 PaintTabBackground(canvas); | |
213 PaintTabBorder(canvas); | |
214 } | |
215 PaintTabTitle(canvas, selected); | |
216 } | |
217 | |
218 bool Tab::OnMousePressed(const ui::MouseEvent& event) { | |
219 SetTitleColor(kTabTitleColor_Pressed); | |
220 return true; | |
221 } | |
222 | |
223 void Tab::OnMouseReleased(const ui::MouseEvent& event) { | |
224 SetTitleColor(kTabTitleColor_Hovered); | |
225 tab_strip_->SelectTab(this); | |
226 } | |
227 | |
228 void Tab::OnGestureEvent(ui::GestureEvent* event) { | |
229 switch (event->type()) { | |
230 case ui::ET_GESTURE_TAP_DOWN: | |
231 SetTitleColor(kTabTitleColor_Pressed); | |
232 break; | |
233 case ui::ET_GESTURE_TAP: | |
234 // SelectTab also sets the right tab color. | |
235 tab_strip_->SelectTab(this); | |
236 break; | |
237 case ui::ET_GESTURE_TAP_CANCEL: | |
238 SetTitleColor(tab_strip_->IsTabSelected(this) ? kTabTitleColor_Active : | |
239 kTabTitleColor_Inactive); | |
240 break; | |
241 default: | |
242 break; | |
243 } | |
244 event->SetHandled(); | |
245 } | |
246 | |
247 void Tab::OnMouseCaptureLost() { | |
248 SetTitleColor(kTabTitleColor_Inactive); | |
249 } | |
250 | |
251 void Tab::OnMouseEntered(const ui::MouseEvent& event) { | |
252 SetTitleColor(tab_strip_->IsTabSelected(this) ? kTabTitleColor_Active : | |
253 kTabTitleColor_Hovered); | |
254 } | |
255 | |
256 void Tab::OnMouseExited(const ui::MouseEvent& event) { | |
257 SetTitleColor(tab_strip_->IsTabSelected(this) ? kTabTitleColor_Active : | |
258 kTabTitleColor_Inactive); | |
259 } | |
260 | |
261 // Custom layout manager that takes care of sizing and displaying the tab pages. | |
262 class TabLayout : public LayoutManager { | |
263 public: | |
264 TabLayout() {} | |
265 | |
266 // Switches to the tab page identified. | |
267 void SwitchToPage(View* host, View* page) { | |
268 for (int i = 0; i < host->child_count(); ++i) { | |
269 View* child = host->child_at(i); | |
270 // The child might not have been laid out yet. | |
271 if (child == page) | |
272 child->SetBoundsRect(host->GetContentsBounds()); | |
273 child->SetVisible(child == page); | |
274 } | |
275 | |
276 FocusManager* focus_manager = page->GetFocusManager(); | |
277 if (focus_manager) { | |
278 const View* focused_view = focus_manager->GetFocusedView(); | |
279 if (focused_view && host->Contains(focused_view) && | |
280 !page->Contains(focused_view)) | |
281 focus_manager->SetFocusedView(page); | |
282 } | |
283 } | |
284 | |
285 private: | |
286 // LayoutManager overrides: | |
287 virtual void Layout(View* host) OVERRIDE { | |
288 gfx::Rect bounds(host->GetContentsBounds()); | |
289 for (int i = 0; i < host->child_count(); ++i) { | |
290 View* child = host->child_at(i); | |
291 // We only layout visible children, since it may be expensive. | |
292 if (child->visible() && child->bounds() != bounds) | |
293 child->SetBoundsRect(bounds); | |
294 } | |
295 } | |
296 | |
297 virtual gfx::Size GetPreferredSize(View* host) OVERRIDE { | |
298 // First, query the preferred sizes to determine a good width. | |
299 int width = 0; | |
300 for (int i = 0; i < host->child_count(); ++i) { | |
301 View* page = host->child_at(i); | |
302 width = std::max(width, page->GetPreferredSize().width()); | |
303 } | |
304 // After we know the width, decide on the height. | |
305 return gfx::Size(width, GetPreferredHeightForWidth(host, width)); | |
306 } | |
307 | |
308 virtual int GetPreferredHeightForWidth(View* host, int width) OVERRIDE { | |
309 int height = 0; | |
310 for (int i = 0; i < host->child_count(); ++i) { | |
311 View* page = host->child_at(i); | |
312 height = std::max(height, page->GetHeightForWidth(width)); | |
313 } | |
314 return height; | |
315 } | |
316 | |
317 DISALLOW_COPY_AND_ASSIGN(TabLayout); | |
318 }; | |
319 | |
320 //////////////////////////////////////////////////////////////////////////////// | |
321 // NativeTabbedPaneViews, public: | |
322 | |
323 NativeTabbedPaneViews::NativeTabbedPaneViews(TabbedPane* tabbed_pane) | |
324 : tabbed_pane_(tabbed_pane), | |
325 tab_layout_manager_(new TabLayout), | |
326 ALLOW_THIS_IN_INITIALIZER_LIST(tab_strip_(new TabStrip(this))), | |
327 content_view_(new views::View) { | |
328 AddChildView(tab_strip_); | |
329 AddChildView(content_view_); | |
330 InitControl(); | |
331 } | |
332 | |
333 NativeTabbedPaneViews::~NativeTabbedPaneViews() { | |
334 } | |
335 | |
336 void NativeTabbedPaneViews::TabSelectionChanged(View* selected) { | |
337 tab_layout_manager_->SwitchToPage(content_view_, Tab::GetContents(selected)); | |
338 if (tabbed_pane_->listener()) | |
339 tabbed_pane_->listener()->TabSelectedAt(tab_strip_->GetIndexOf(selected)); | |
340 } | |
341 | |
342 //////////////////////////////////////////////////////////////////////////////// | |
343 // NativeTabbedPaneViews, NativeTabbedPaneWrapper implementation: | |
344 | |
345 void NativeTabbedPaneViews::AddTab(const string16& title, View* contents) { | |
346 AddTabAtIndex(tab_strip_->child_count(), title, contents, true); | |
347 } | |
348 | |
349 void NativeTabbedPaneViews::AddTabAtIndex(int index, | |
350 const string16& title, | |
351 View* contents, | |
352 bool select_if_first_tab) { | |
353 DCHECK(index <= static_cast<int>(tab_strip_->child_count())); | |
354 contents->set_owned_by_client(); | |
355 contents->SetVisible(false); | |
356 | |
357 tab_strip_->AddChildViewAt(new Tab(tab_strip_, title, contents), index); | |
358 | |
359 // Add native tab only if the native control is already created. | |
360 content_view_->AddChildViewAt(contents, index); | |
361 | |
362 // Switch to the newly added tab if requested; | |
363 if (tab_strip_->child_count() == 1 && select_if_first_tab) | |
364 tab_layout_manager_->SwitchToPage(content_view_, contents); | |
365 if (!tab_strip_->selected_tab()) | |
366 tab_strip_->SelectTab(tab_strip_->child_at(0)); | |
367 } | |
368 | |
369 View* NativeTabbedPaneViews::RemoveTabAtIndex(int index) { | |
370 DCHECK(index >= 0 && index < tab_strip_->child_count()); | |
371 | |
372 if (index < (tab_strip_->child_count() - 1)) { | |
373 // Select the next tab. | |
374 SelectTabAt(index + 1); | |
375 } else { | |
376 // We are the last tab, select the previous one. | |
377 if (index > 0) { | |
378 SelectTabAt(index - 1); | |
379 } else { | |
380 // That was the last tab. Remove the contents. | |
381 content_view_->RemoveAllChildViews(false); | |
382 } | |
383 } | |
384 return tab_strip_->RemoveTabAt(index); | |
385 } | |
386 | |
387 void NativeTabbedPaneViews::SelectTabAt(int index) { | |
388 DCHECK((index >= 0) && (index < tab_strip_->child_count())); | |
389 tab_strip_->SelectTab(tab_strip_->child_at(index)); | |
390 } | |
391 | |
392 int NativeTabbedPaneViews::GetTabCount() { | |
393 return tab_strip_->child_count(); | |
394 } | |
395 | |
396 int NativeTabbedPaneViews::GetSelectedTabIndex() { | |
397 return tab_strip_->GetSelectedIndex(); | |
398 } | |
399 | |
400 View* NativeTabbedPaneViews::GetSelectedTab() { | |
401 return content_view_->child_at(GetSelectedTabIndex()); | |
402 } | |
403 | |
404 View* NativeTabbedPaneViews::GetView() { | |
405 return this; | |
406 } | |
407 | |
408 void NativeTabbedPaneViews::SetFocus() { | |
409 // Focus the associated HWND. | |
410 OnFocus(); | |
411 } | |
412 | |
413 gfx::Size NativeTabbedPaneViews::GetPreferredSize() { | |
414 gfx::Size content_size = content_view_->GetPreferredSize(); | |
415 return gfx::Size( | |
416 content_size.width(), | |
417 tab_strip_->GetPreferredSize().height() + content_size.height()); | |
418 } | |
419 | |
420 gfx::NativeView NativeTabbedPaneViews::GetTestingHandle() const { | |
421 return NULL; | |
422 } | |
423 | |
424 //////////////////////////////////////////////////////////////////////////////// | |
425 // NativeTabbedPaneViews, View overrides: | |
426 | |
427 void NativeTabbedPaneViews::Layout() { | |
428 gfx::Size ps = tab_strip_->GetPreferredSize(); | |
429 tab_strip_->SetBounds(0, 0, width(), ps.height()); | |
430 content_view_->SetBounds(0, tab_strip_->bounds().bottom(), width(), | |
431 std::max(0, height() - ps.height())); | |
432 } | |
433 | |
434 //////////////////////////////////////////////////////////////////////////////// | |
435 // NativeTabbedPaneViews, private: | |
436 | |
437 void NativeTabbedPaneViews::InitControl() { | |
438 content_view_->SetLayoutManager(tab_layout_manager_); | |
439 | |
440 // Add tabs that are already added if any. | |
441 if (tab_strip_->has_children()) | |
442 InitializeTabs(); | |
443 } | |
444 | |
445 void NativeTabbedPaneViews::InitializeTabs() { | |
446 for (int i = 0; i < tab_strip_->child_count(); ++i) | |
447 content_view_->AddChildView(Tab::GetContents(tab_strip_->child_at(i))); | |
448 } | |
449 | |
450 } // namespace views | |
OLD | NEW |