| 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 |