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 "chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h" 6 7 #include "ash/shell_delegate.h" 8 #include "ash/wm/frame_painter.h" 9 #include "ash/wm/workspace/frame_maximize_button.h" 10 #include "chrome/browser/themes/theme_properties.h" 11 #include "chrome/browser/ui/ash/chrome_shell_delegate.h" 12 #include "chrome/browser/ui/browser.h" 13 #include "chrome/browser/ui/immersive_fullscreen_configuration.h" 14 #include "chrome/browser/ui/views/avatar_menu_button.h" 15 #include "chrome/browser/ui/views/frame/browser_frame.h" 16 #include "chrome/browser/ui/views/frame/browser_view.h" 17 #include "chrome/browser/ui/views/frame/immersive_mode_controller.h" 18 #include "chrome/browser/ui/views/tab_icon_view.h" 19 #include "chrome/browser/ui/views/tabs/tab_strip.h" 20 #include "content/public/browser/web_contents.h" 21 #include "grit/ash_resources.h" 22 #include "grit/generated_resources.h" // Accessibility names 23 #include "grit/theme_resources.h" 24 #include "ui/aura/client/aura_constants.h" 25 #include "ui/aura/window.h" 26 #include "ui/base/accessibility/accessible_view_state.h" 27 #include "ui/base/hit_test.h" 28 #include "ui/base/l10n/l10n_util.h" 29 #include "ui/base/layout.h" 30 #include "ui/base/resource/resource_bundle.h" 31 #include "ui/base/theme_provider.h" 32 #include "ui/compositor/layer_animator.h" 33 #include "ui/compositor/scoped_animation_duration_scale_mode.h" 34 #include "ui/gfx/canvas.h" 35 #include "ui/gfx/image/image_skia.h" 36 #include "ui/views/controls/button/image_button.h" 37 #include "ui/views/controls/label.h" 38 #include "ui/views/layout/layout_constants.h" 39 #include "ui/views/widget/widget.h" 40 #include "ui/views/widget/widget_delegate.h" 41 42 namespace { 43 44 // The avatar ends 2 px above the bottom of the tabstrip (which, given the 45 // way the tabstrip draws its bottom edge, will appear like a 1 px gap to the 46 // user). 47 const int kAvatarBottomSpacing = 2; 48 // There are 2 px on each side of the avatar (between the frame border and 49 // it on the left, and between it and the tabstrip on the right). 50 const int kAvatarSideSpacing = 2; 51 // Space between left edge of window and tabstrip. 52 const int kTabstripLeftSpacing = 0; 53 // Space between right edge of tabstrip and maximize button. 54 const int kTabstripRightSpacing = 10; 55 // Height of the shadow of the content area, at the top of the toolbar. 56 const int kContentShadowHeight = 1; 57 // Space between top of window and top of tabstrip for tall headers, such as 58 // for restored windows, apps, etc. 59 const int kTabstripTopSpacingTall = 7; 60 // Space between top of window and top of tabstrip for short headers, such as 61 // for maximized windows, pop-ups, etc. 62 const int kTabstripTopSpacingShort = 0; 63 // Height of the shadow in the tab image, used to ensure clicks in the shadow 64 // area still drag restored windows. This keeps the clickable area large enough 65 // to hit easily. 66 const int kTabShadowHeight = 4; 67 68 } // namespace 69 70 /////////////////////////////////////////////////////////////////////////////// 71 // BrowserNonClientFrameViewAsh, public: 72 73 // static 74 const char BrowserNonClientFrameViewAsh::kViewClassName[] = 75 "BrowserNonClientFrameViewAsh"; 76 77 BrowserNonClientFrameViewAsh::BrowserNonClientFrameViewAsh( 78 BrowserFrame* frame, BrowserView* browser_view) 79 : BrowserNonClientFrameView(frame, browser_view), 80 size_button_(NULL), 81 close_button_(NULL), 82 window_icon_(NULL), 83 frame_painter_(new ash::FramePainter), 84 size_button_minimizes_(false) { 85 } 86 87 BrowserNonClientFrameViewAsh::~BrowserNonClientFrameViewAsh() { 88 } 89 90 void BrowserNonClientFrameViewAsh::Init() { 91 // Panels only minimize. 92 ash::FramePainter::SizeButtonBehavior size_button_behavior; 93 size_button_ = new ash::FrameMaximizeButton(this, this); 94 size_button_behavior = ash::FramePainter::SIZE_BUTTON_MAXIMIZES; 95 size_button_->SetAccessibleName( 96 l10n_util::GetStringUTF16(IDS_ACCNAME_MAXIMIZE)); 97 AddChildView(size_button_); 98 close_button_ = new views::ImageButton(this); 99 close_button_->SetAccessibleName( 100 l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE)); 101 AddChildView(close_button_); 102 103 // Initializing the TabIconView is expensive, so only do it if we need to. 104 if (browser_view()->ShouldShowWindowIcon()) { 105 window_icon_ = new TabIconView(this); 106 window_icon_->set_is_light(true); 107 AddChildView(window_icon_); 108 window_icon_->Update(); 109 } 110 111 // Create incognito icon if necessary. 112 UpdateAvatarInfo(); 113 114 // Frame painter handles layout of these buttons. 115 frame_painter_->Init(frame(), window_icon_, size_button_, close_button_, 116 size_button_behavior); 117 } 118 119 /////////////////////////////////////////////////////////////////////////////// 120 // BrowserNonClientFrameView overrides: 121 122 gfx::Rect BrowserNonClientFrameViewAsh::GetBoundsForTabStrip( 123 views::View* tabstrip) const { 124 if (!tabstrip) 125 return gfx::Rect(); 126 127 // When the tab strip is painted in the immersive fullscreen light bar style, 128 // the caption buttons and the avatar button are not visible. However, their 129 // bounds are still used to compute the tab strip bounds so that the tabs have 130 // the same horizontal position when the tab strip is painted in the immersive 131 // light bar style as when the top-of-window views are revealed. 132 TabStripInsets insets(GetTabStripInsets(false)); 133 return gfx::Rect(insets.left, insets.top, 134 std::max(0, width() - insets.left - insets.right), 135 tabstrip->GetPreferredSize().height()); 136 } 137 138 BrowserNonClientFrameView::TabStripInsets 139 BrowserNonClientFrameViewAsh::GetTabStripInsets(bool force_restored) const { 140 int left = avatar_button() ? kAvatarSideSpacing + 141 browser_view()->GetOTRAvatarIcon().width() + kAvatarSideSpacing : 142 kTabstripLeftSpacing; 143 int right = frame_painter_->GetRightInset() + kTabstripRightSpacing; 144 return TabStripInsets(NonClientTopBorderHeight(force_restored), left, right); 145 } 146 147 int BrowserNonClientFrameViewAsh::GetThemeBackgroundXInset() const { 148 return frame_painter_->GetThemeBackgroundXInset(); 149 } 150 151 void BrowserNonClientFrameViewAsh::UpdateThrobber(bool running) { 152 if (window_icon_) 153 window_icon_->Update(); 154 } 155 156 /////////////////////////////////////////////////////////////////////////////// 157 // views::NonClientFrameView overrides: 158 159 gfx::Rect BrowserNonClientFrameViewAsh::GetBoundsForClientView() const { 160 int top_height = NonClientTopBorderHeight(false); 161 return frame_painter_->GetBoundsForClientView(top_height, bounds()); 162 } 163 164 gfx::Rect BrowserNonClientFrameViewAsh::GetWindowBoundsForClientBounds( 165 const gfx::Rect& client_bounds) const { 166 int top_height = NonClientTopBorderHeight(false); 167 return frame_painter_->GetWindowBoundsForClientBounds(top_height, 168 client_bounds); 169 } 170 171 int BrowserNonClientFrameViewAsh::NonClientHitTest(const gfx::Point& point) { 172 int hit_test = frame_painter_->NonClientHitTest(this, point); 173 // When the window is restored we want a large click target above the tabs 174 // to drag the window, so redirect clicks in the tab's shadow to caption. 175 if (hit_test == HTCLIENT && 176 !(frame()->IsMaximized() || frame()->IsFullscreen())) { 177 // Convert point to client coordinates. 178 gfx::Point client_point(point); 179 View::ConvertPointToTarget(this, frame()->client_view(), &client_point); 180 // Report hits in shadow at top of tabstrip as caption. 181 gfx::Rect tabstrip_bounds(browser_view()->tabstrip()->bounds()); 182 if (client_point.y() < tabstrip_bounds.y() + kTabShadowHeight) 183 hit_test = HTCAPTION; 184 } 185 return hit_test; 186 } 187 188 void BrowserNonClientFrameViewAsh::GetWindowMask(const gfx::Size& size, 189 gfx::Path* window_mask) { 190 // Aura does not use window masks. 191 } 192 193 void BrowserNonClientFrameViewAsh::ResetWindowControls() { 194 // Hide the caption buttons in immersive fullscreen when the tab light bar 195 // is visible because it's confusing when the user hovers or clicks in the 196 // top-right of the screen and hits one. 197 bool button_visibility = !UseImmersiveLightbarHeaderStyle(); 198 size_button_->SetVisible(button_visibility); 199 close_button_->SetVisible(button_visibility); 200 201 size_button_->SetState(views::CustomButton::STATE_NORMAL); 202 // The close button isn't affected by this constraint. 203 } 204 205 void BrowserNonClientFrameViewAsh::UpdateWindowIcon() { 206 if (window_icon_) 207 window_icon_->SchedulePaint(); 208 } 209 210 void BrowserNonClientFrameViewAsh::UpdateWindowTitle() { 211 if (!frame()->IsFullscreen()) 212 frame_painter_->SchedulePaintForTitle(BrowserFrame::GetTitleFont()); 213 } 214 215 /////////////////////////////////////////////////////////////////////////////// 216 // views::View overrides: 217 218 void BrowserNonClientFrameViewAsh::OnPaint(gfx::Canvas* canvas) { 219 if (!ShouldPaint()) 220 return; 221 222 if (UseImmersiveLightbarHeaderStyle()) { 223 PaintImmersiveLightbarStyleHeader(canvas); 224 return; 225 } 226 227 // The primary header image changes based on window activation state and 228 // theme, so we look it up for each paint. 229 int theme_frame_image_id = GetThemeFrameImageId(); 230 int theme_frame_overlay_image_id = GetThemeFrameOverlayImageId(); 231 232 ui::ThemeProvider* theme_provider = GetThemeProvider(); 233 ash::FramePainter::Themed header_themed = ash::FramePainter::THEMED_NO; 234 if (theme_provider->HasCustomImage(theme_frame_image_id) || 235 (theme_frame_overlay_image_id != 0 && 236 theme_provider->HasCustomImage(theme_frame_overlay_image_id))) { 237 header_themed = ash::FramePainter::THEMED_YES; 238 } 239 240 if (frame_painter_->ShouldUseMinimalHeaderStyle(header_themed)) 241 theme_frame_image_id = IDR_AURA_WINDOW_HEADER_BASE_MINIMAL; 242 243 frame_painter_->PaintHeader( 244 this, 245 canvas, 246 ShouldPaintAsActive() ? 247 ash::FramePainter::ACTIVE : ash::FramePainter::INACTIVE, 248 theme_frame_image_id, 249 theme_frame_overlay_image_id); 250 if (browser_view()->ShouldShowWindowTitle()) 251 frame_painter_->PaintTitleBar(this, canvas, BrowserFrame::GetTitleFont()); 252 if (browser_view()->IsToolbarVisible()) 253 PaintToolbarBackground(canvas); 254 else 255 PaintContentEdge(canvas); 256 } 257 258 void BrowserNonClientFrameViewAsh::Layout() { 259 frame_painter_->LayoutHeader(this, UseShortHeader()); 260 if (avatar_button()) 261 LayoutAvatar(); 262 BrowserNonClientFrameView::Layout(); 263 } 264 265 const char* BrowserNonClientFrameViewAsh::GetClassName() const { 266 return kViewClassName; 267 } 268 269 bool BrowserNonClientFrameViewAsh::HitTestRect(const gfx::Rect& rect) const { 270 if (!views::View::HitTestRect(rect)) { 271 // |rect| is outside BrowserNonClientFrameViewAsh's bounds. 272 return false; 273 } 274 // If the rect is outside the bounds of the client area, claim it. 275 // TODO(tdanderson): Implement View::ConvertRectToTarget(). 276 gfx::Point rect_in_client_view_coords_origin(rect.origin()); 277 View::ConvertPointToTarget(this, frame()->client_view(), 278 &rect_in_client_view_coords_origin); 279 gfx::Rect rect_in_client_view_coords( 280 rect_in_client_view_coords_origin, rect.size()); 281 if (!frame()->client_view()->HitTestRect(rect_in_client_view_coords)) 282 return true; 283 284 // Otherwise, claim |rect| only if it is above the bottom of the tabstrip in 285 // a non-tab portion. 286 TabStrip* tabstrip = browser_view()->tabstrip(); 287 if (!tabstrip || !browser_view()->IsTabStripVisible()) 288 return false; 289 290 gfx::Point rect_in_tabstrip_coords_origin(rect.origin()); 291 View::ConvertPointToTarget(this, tabstrip, 292 &rect_in_tabstrip_coords_origin); 293 gfx::Rect rect_in_tabstrip_coords(rect_in_tabstrip_coords_origin, 294 rect.size()); 295 296 if (rect_in_tabstrip_coords.bottom() > tabstrip->GetLocalBounds().bottom()) { 297 // |rect| is below the tabstrip. 298 return false; 299 } 300 301 if (tabstrip->HitTestRect(rect_in_tabstrip_coords)) { 302 // Claim |rect| if it is in a non-tab portion of the tabstrip. 303 // TODO(tdanderson): Pass |rect_in_tabstrip_coords| instead of its center 304 // point to TabStrip::IsPositionInWindowCaption() once 305 // GetEventHandlerForRect() is implemented. 306 return tabstrip->IsPositionInWindowCaption( 307 rect_in_tabstrip_coords.CenterPoint()); 308 } 309 310 // We claim |rect| because it is above the bottom of the tabstrip, but 311 // not in the tabstrip. In particular, the window controls are right of 312 // the tabstrip. 313 return true; 314 } 315 316 void BrowserNonClientFrameViewAsh::GetAccessibleState( 317 ui::AccessibleViewState* state) { 318 state->role = ui::AccessibilityTypes::ROLE_TITLEBAR; 319 } 320 321 gfx::Size BrowserNonClientFrameViewAsh::GetMinimumSize() { 322 return frame_painter_->GetMinimumSize(this); 323 } 324 325 void BrowserNonClientFrameViewAsh::OnThemeChanged() { 326 BrowserNonClientFrameView::OnThemeChanged(); 327 frame_painter_->OnThemeChanged(); 328 } 329 330 /////////////////////////////////////////////////////////////////////////////// 331 // views::ButtonListener overrides: 332 333 void BrowserNonClientFrameViewAsh::ButtonPressed(views::Button* sender, 334 const ui::Event& event) { 335 // When shift-clicking slow down animations for visual debugging. 336 // We used to do this via an event filter that looked for the shift key being 337 // pressed but this interfered with several normal keyboard shortcuts. 338 scoped_ptr<ui::ScopedAnimationDurationScaleMode> slow_duration_mode; 339 if (event.IsShiftDown()) { 340 slow_duration_mode.reset(new ui::ScopedAnimationDurationScaleMode( 341 ui::ScopedAnimationDurationScaleMode::SLOW_DURATION)); 342 } 343 344 ash::UserMetricsAction action = 345 ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MAXIMIZE; 346 347 if (sender == size_button_) { 348 // The maximize button may move out from under the cursor. 349 ResetWindowControls(); 350 if (size_button_minimizes_) { 351 frame()->Minimize(); 352 action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MINIMIZE; 353 } else if (frame()->IsFullscreen()) { // Can be clicked in immersive mode. 354 frame()->SetFullscreen(false); 355 action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_EXIT_FULLSCREEN; 356 } else if (frame()->IsMaximized()) { 357 frame()->Restore(); 358 action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_RESTORE; 359 } else { 360 frame()->Maximize(); 361 } 362 // |this| may be deleted - some windows delete their frames on maximize. 363 } else if (sender == close_button_) { 364 frame()->Close(); 365 action = ash::UMA_WINDOW_CLOSE_BUTTON_CLICK; 366 } else { 367 return; 368 } 369 ChromeShellDelegate::instance()->RecordUserMetricsAction(action); 370 } 371 372 /////////////////////////////////////////////////////////////////////////////// 373 // chrome::TabIconViewModel overrides: 374 375 bool BrowserNonClientFrameViewAsh::ShouldTabIconViewAnimate() const { 376 // This function is queried during the creation of the window as the 377 // TabIconView we host is initialized, so we need to NULL check the selected 378 // WebContents because in this condition there is not yet a selected tab. 379 content::WebContents* current_tab = browser_view()->GetActiveWebContents(); 380 return current_tab ? current_tab->IsLoading() : false; 381 } 382 383 gfx::ImageSkia BrowserNonClientFrameViewAsh::GetFaviconForTabIconView() { 384 views::WidgetDelegate* delegate = frame()->widget_delegate(); 385 if (!delegate) 386 return gfx::ImageSkia(); 387 return delegate->GetWindowIcon(); 388 } 389 390 /////////////////////////////////////////////////////////////////////////////// 391 // BrowserNonClientFrameViewAsh, private: 392 393 394 int BrowserNonClientFrameViewAsh::NonClientTopBorderHeight( 395 bool force_restored) const { 396 if (force_restored) 397 return kTabstripTopSpacingTall; 398 if (!ShouldPaint() || UseImmersiveLightbarHeaderStyle()) 399 return 0; 400 // Windows with tab strips need a smaller non-client area. 401 if (browser_view()->IsTabStripVisible()) { 402 if (UseShortHeader()) 403 return kTabstripTopSpacingShort; 404 return kTabstripTopSpacingTall; 405 } 406 // For windows without a tab strip (popups, etc.) ensure we have enough space 407 // to see the window caption buttons. 408 return close_button_->bounds().bottom() - kContentShadowHeight; 409 } 410 411 bool BrowserNonClientFrameViewAsh::UseShortHeader() const { 412 // Restored browser -> tall header 413 // Maximized browser -> short header 414 // Fullscreen browser, no immersive reveal -> hidden or super short light bar 415 // Fullscreen browser, immersive reveal -> short header 416 // Popup&App window -> tall header 417 // Panel -> short header 418 // Dialogs use short header and are handled via CustomFrameViewAsh. 419 Browser* browser = browser_view()->browser(); 420 switch (browser->type()) { 421 case Browser::TYPE_TABBED: 422 return frame()->IsMaximized() || frame()->IsFullscreen(); 423 case Browser::TYPE_POPUP: 424 return false; 425 default: 426 NOTREACHED(); 427 return false; 428 } 429 } 430 431 bool BrowserNonClientFrameViewAsh::UseImmersiveLightbarHeaderStyle() const { 432 ImmersiveModeController* immersive_controller = 433 browser_view()->immersive_mode_controller(); 434 return immersive_controller->IsEnabled() && 435 !immersive_controller->IsRevealed() && 436 !immersive_controller->ShouldHideTabIndicators(); 437 } 438 439 void BrowserNonClientFrameViewAsh::LayoutAvatar() { 440 DCHECK(avatar_button()); 441 gfx::ImageSkia incognito_icon = browser_view()->GetOTRAvatarIcon(); 442 443 int avatar_bottom = GetTabStripInsets(false).top + 444 browser_view()->GetTabStripHeight() - kAvatarBottomSpacing; 445 int avatar_restored_y = avatar_bottom - incognito_icon.height(); 446 int avatar_y = (frame()->IsMaximized() || frame()->IsFullscreen()) ? 447 NonClientTopBorderHeight(false) + kContentShadowHeight : 448 avatar_restored_y; 449 450 // Hide the incognito icon in immersive fullscreen when the tab light bar is 451 // visible because the header is too short for the icognito icon to be 452 // recognizable. 453 bool avatar_visible = !UseImmersiveLightbarHeaderStyle(); 454 int avatar_height = avatar_visible ? avatar_bottom - avatar_y : 0; 455 456 gfx::Rect avatar_bounds(kAvatarSideSpacing, 457 avatar_y, 458 incognito_icon.width(), 459 avatar_height); 460 avatar_button()->SetBoundsRect(avatar_bounds); 461 avatar_button()->SetVisible(avatar_visible); 462 } 463 464 bool BrowserNonClientFrameViewAsh::ShouldPaint() const { 465 if (!frame()->IsFullscreen()) 466 return true; 467 468 // There is nothing to paint for traditional (tab) fullscreen. 469 ImmersiveModeController* immersive_controller = 470 browser_view()->immersive_mode_controller(); 471 if (!immersive_controller->IsEnabled()) 472 return false; 473 474 // Need to paint during an immersive fullscreen reveal or when the immersive 475 // light bar is visible. 476 return immersive_controller->IsRevealed() || 477 !immersive_controller->ShouldHideTabIndicators(); 478 } 479 480 void BrowserNonClientFrameViewAsh::PaintImmersiveLightbarStyleHeader( 481 gfx::Canvas* canvas) { 482 // The light bar header is not themed because theming it does not look good. 483 gfx::ImageSkia* frame_image = GetThemeProvider()->GetImageSkiaNamed( 484 IDR_AURA_WINDOW_HEADER_BASE_MINIMAL); 485 canvas->TileImageInt(*frame_image, 0, 0, width(), frame_image->height()); 486 } 487 488 void BrowserNonClientFrameViewAsh::PaintToolbarBackground(gfx::Canvas* canvas) { 489 gfx::Rect toolbar_bounds(browser_view()->GetToolbarBounds()); 490 if (toolbar_bounds.IsEmpty()) 491 return; 492 gfx::Point toolbar_origin(toolbar_bounds.origin()); 493 View::ConvertPointToTarget(browser_view(), this, &toolbar_origin); 494 toolbar_bounds.set_origin(toolbar_origin); 495 496 int x = toolbar_bounds.x(); 497 int w = toolbar_bounds.width(); 498 int y = toolbar_bounds.y(); 499 int h = toolbar_bounds.height(); 500 501 // Gross hack: We split the toolbar images into two pieces, since sometimes 502 // (popup mode) the toolbar isn't tall enough to show the whole image. The 503 // split happens between the top shadow section and the bottom gradient 504 // section so that we never break the gradient. 505 int split_point = kFrameShadowThickness * 2; 506 int bottom_y = y + split_point; 507 ui::ThemeProvider* tp = GetThemeProvider(); 508 int bottom_edge_height = h - split_point; 509 510 canvas->FillRect(gfx::Rect(x, bottom_y, w, bottom_edge_height), 511 tp->GetColor(ThemeProperties::COLOR_TOOLBAR)); 512 513 // Paint the main toolbar image. Since this image is also used to draw the 514 // tab background, we must use the tab strip offset to compute the image 515 // source y position. If you have to debug this code use an image editor 516 // to paint a diagonal line through the toolbar image and ensure it lines up 517 // across the tab and toolbar. 518 gfx::ImageSkia* theme_toolbar = tp->GetImageSkiaNamed(IDR_THEME_TOOLBAR); 519 canvas->TileImageInt( 520 *theme_toolbar, 521 x + GetThemeBackgroundXInset(), 522 bottom_y - GetTabStripInsets(false).top, 523 x, bottom_y, 524 w, theme_toolbar->height()); 525 526 // The content area line has a shadow that extends a couple of pixels above 527 // the toolbar bounds. 528 const int kContentShadowHeight = 2; 529 gfx::ImageSkia* toolbar_top = tp->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_TOP); 530 canvas->TileImageInt(*toolbar_top, 531 0, 0, 532 x, y - kContentShadowHeight, 533 w, split_point + kContentShadowHeight + 1); 534 535 // Draw the "lightening" shade line around the edges of the toolbar. 536 gfx::ImageSkia* toolbar_left = tp->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_LEFT); 537 canvas->TileImageInt(*toolbar_left, 538 0, 0, 539 x + kClientEdgeThickness, 540 y + kClientEdgeThickness + kContentShadowHeight, 541 toolbar_left->width(), theme_toolbar->height()); 542 gfx::ImageSkia* toolbar_right = 543 tp->GetImageSkiaNamed(IDR_TOOLBAR_SHADE_RIGHT); 544 canvas->TileImageInt(*toolbar_right, 545 0, 0, 546 w - toolbar_right->width() - 2 * kClientEdgeThickness, 547 y + kClientEdgeThickness + kContentShadowHeight, 548 toolbar_right->width(), theme_toolbar->height()); 549 550 // Draw the content/toolbar separator. 551 canvas->FillRect( 552 gfx::Rect(x + kClientEdgeThickness, 553 toolbar_bounds.bottom() - kClientEdgeThickness, 554 w - (2 * kClientEdgeThickness), 555 kClientEdgeThickness), 556 ThemeProperties::GetDefaultColor( 557 ThemeProperties::COLOR_TOOLBAR_SEPARATOR)); 558 } 559 560 void BrowserNonClientFrameViewAsh::PaintContentEdge(gfx::Canvas* canvas) { 561 canvas->FillRect(gfx::Rect(0, close_button_->bounds().bottom(), 562 width(), kClientEdgeThickness), 563 ThemeProperties::GetDefaultColor( 564 ThemeProperties::COLOR_TOOLBAR_SEPARATOR)); 565 } 566 567 int BrowserNonClientFrameViewAsh::GetThemeFrameImageId() const { 568 bool is_incognito = browser_view()->IsOffTheRecord() && 569 !browser_view()->IsGuestSession(); 570 if (browser_view()->IsBrowserTypeNormal()) { 571 // Use the standard resource ids to allow users to theme the frames. 572 if (ShouldPaintAsActive()) { 573 return is_incognito ? 574 IDR_THEME_FRAME_INCOGNITO : IDR_THEME_FRAME; 575 } 576 return is_incognito ? 577 IDR_THEME_FRAME_INCOGNITO_INACTIVE : IDR_THEME_FRAME_INACTIVE; 578 } 579 // Never theme app and popup windows. 580 if (ShouldPaintAsActive()) { 581 return is_incognito ? 582 IDR_AURA_WINDOW_HEADER_BASE_INCOGNITO_ACTIVE : 583 IDR_AURA_WINDOW_HEADER_BASE_ACTIVE; 584 } 585 return is_incognito ? 586 IDR_AURA_WINDOW_HEADER_BASE_INCOGNITO_INACTIVE : 587 IDR_AURA_WINDOW_HEADER_BASE_INACTIVE; 588 } 589 590 int BrowserNonClientFrameViewAsh::GetThemeFrameOverlayImageId() const { 591 ui::ThemeProvider* tp = GetThemeProvider(); 592 if (tp->HasCustomImage(IDR_THEME_FRAME_OVERLAY) && 593 browser_view()->IsBrowserTypeNormal() && 594 !browser_view()->IsOffTheRecord()) { 595 return ShouldPaintAsActive() ? 596 IDR_THEME_FRAME_OVERLAY : IDR_THEME_FRAME_OVERLAY_INACTIVE; 597 } 598 return 0; 599 } 600