opaque_browser_frame_view.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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/opaque_browser_frame_view.h" 6 7#include <algorithm> 8#include <string> 9 10#include "base/compiler_specific.h" 11#include "base/prefs/pref_service.h" 12#include "base/strings/utf_string_conversions.h" 13#include "chrome/browser/chrome_notification_types.h" 14#include "chrome/browser/profiles/profiles_state.h" 15#include "chrome/browser/signin/signin_header_helper.h" 16#include "chrome/browser/themes/theme_properties.h" 17#include "chrome/browser/ui/views/frame/browser_frame.h" 18#include "chrome/browser/ui/views/frame/browser_view.h" 19#include "chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h" 20#include "chrome/browser/ui/views/frame/opaque_browser_frame_view_platform_specific.h" 21#include "chrome/browser/ui/views/profiles/avatar_label.h" 22#include "chrome/browser/ui/views/profiles/avatar_menu_button.h" 23#include "chrome/browser/ui/views/profiles/new_avatar_button.h" 24#include "chrome/browser/ui/views/tab_icon_view.h" 25#include "chrome/browser/ui/views/tabs/tab_strip.h" 26#include "chrome/browser/ui/views/theme_image_mapper.h" 27#include "chrome/browser/ui/views/toolbar/toolbar_view.h" 28#include "chrome/common/pref_names.h" 29#include "components/signin/core/common/profile_management_switches.h" 30#include "content/public/browser/notification_service.h" 31#include "content/public/browser/web_contents.h" 32#include "grit/chromium_strings.h" 33#include "grit/generated_resources.h" 34#include "grit/theme_resources.h" 35#include "grit/ui_resources.h" 36#include "ui/accessibility/ax_view_state.h" 37#include "ui/base/hit_test.h" 38#include "ui/base/l10n/l10n_util.h" 39#include "ui/base/resource/resource_bundle.h" 40#include "ui/base/theme_provider.h" 41#include "ui/gfx/canvas.h" 42#include "ui/gfx/font_list.h" 43#include "ui/gfx/image/image.h" 44#include "ui/gfx/image/image_skia.h" 45#include "ui/gfx/path.h" 46#include "ui/gfx/rect_conversions.h" 47#include "ui/views/controls/button/image_button.h" 48#include "ui/views/controls/image_view.h" 49#include "ui/views/controls/label.h" 50#include "ui/views/layout/layout_constants.h" 51#include "ui/views/views_delegate.h" 52#include "ui/views/widget/root_view.h" 53#include "ui/views/window/frame_background.h" 54#include "ui/views/window/window_shape.h" 55 56#if defined(OS_LINUX) 57#include "ui/views/controls/menu/menu_runner.h" 58#endif 59 60using content::WebContents; 61 62namespace { 63 64// While resize areas on Windows are normally the same size as the window 65// borders, our top area is shrunk by 1 px to make it easier to move the window 66// around with our thinner top grabbable strip. (Incidentally, our side and 67// bottom resize areas don't match the frame border thickness either -- they 68// span the whole nonclient area, so there's no "dead zone" for the mouse.) 69const int kTopResizeAdjust = 1; 70 71// In the window corners, the resize areas don't actually expand bigger, but the 72// 16 px at the end of each edge triggers diagonal resizing. 73const int kResizeAreaCornerSize = 16; 74 75// The content left/right images have a shadow built into them. 76const int kContentEdgeShadowThickness = 2; 77 78// The icon never shrinks below 16 px on a side. 79const int kIconMinimumSize = 16; 80 81#if defined(OS_LINUX) && !defined(OS_CHROMEOS) 82// The number of pixels to move the frame background image upwards when using 83// the GTK+ theme and the titlebar is condensed. 84const int kGTKThemeCondensedFrameTopInset = 15; 85#endif 86 87} // namespace 88 89/////////////////////////////////////////////////////////////////////////////// 90// OpaqueBrowserFrameView, public: 91 92OpaqueBrowserFrameView::OpaqueBrowserFrameView(BrowserFrame* frame, 93 BrowserView* browser_view) 94 : BrowserNonClientFrameView(frame, browser_view), 95 layout_(new OpaqueBrowserFrameViewLayout(this)), 96 minimize_button_(NULL), 97 maximize_button_(NULL), 98 restore_button_(NULL), 99 close_button_(NULL), 100 window_icon_(NULL), 101 window_title_(NULL), 102 frame_background_(new views::FrameBackground()) { 103 SetLayoutManager(layout_); 104 105 minimize_button_ = InitWindowCaptionButton(IDR_MINIMIZE, 106 IDR_MINIMIZE_H, 107 IDR_MINIMIZE_P, 108 IDR_MINIMIZE_BUTTON_MASK, 109 IDS_ACCNAME_MINIMIZE, 110 VIEW_ID_MINIMIZE_BUTTON); 111 maximize_button_ = InitWindowCaptionButton(IDR_MAXIMIZE, 112 IDR_MAXIMIZE_H, 113 IDR_MAXIMIZE_P, 114 IDR_MAXIMIZE_BUTTON_MASK, 115 IDS_ACCNAME_MAXIMIZE, 116 VIEW_ID_MAXIMIZE_BUTTON); 117 restore_button_ = InitWindowCaptionButton(IDR_RESTORE, 118 IDR_RESTORE_H, 119 IDR_RESTORE_P, 120 IDR_RESTORE_BUTTON_MASK, 121 IDS_ACCNAME_RESTORE, 122 VIEW_ID_RESTORE_BUTTON); 123 close_button_ = InitWindowCaptionButton(IDR_CLOSE, 124 IDR_CLOSE_H, 125 IDR_CLOSE_P, 126 IDR_CLOSE_BUTTON_MASK, 127 IDS_ACCNAME_CLOSE, 128 VIEW_ID_CLOSE_BUTTON); 129 130 // Initializing the TabIconView is expensive, so only do it if we need to. 131 if (browser_view->ShouldShowWindowIcon()) { 132 window_icon_ = new TabIconView(this, this); 133 window_icon_->set_is_light(true); 134 window_icon_->set_id(VIEW_ID_WINDOW_ICON); 135 AddChildView(window_icon_); 136 window_icon_->Update(); 137 } 138 139 window_title_ = new views::Label( 140 browser_view->GetWindowTitle(), 141 gfx::FontList(BrowserFrame::GetTitleFontList())); 142 window_title_->SetVisible(browser_view->ShouldShowWindowTitle()); 143 window_title_->SetEnabledColor(SK_ColorWHITE); 144 window_title_->set_subpixel_rendering_enabled(false); 145 window_title_->SetHorizontalAlignment(gfx::ALIGN_LEFT); 146 window_title_->set_id(VIEW_ID_WINDOW_TITLE); 147 AddChildView(window_title_); 148 149 if (browser_view->IsRegularOrGuestSession() && switches::IsNewAvatarMenu()) 150 UpdateNewStyleAvatarInfo(this, NewAvatarButton::THEMED_BUTTON); 151 else 152 UpdateAvatarInfo(); 153 154 if (!browser_view->IsOffTheRecord()) { 155 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, 156 content::NotificationService::AllSources()); 157 } 158 159 platform_observer_.reset(OpaqueBrowserFrameViewPlatformSpecific::Create( 160 this, layout_, browser_view->browser()->profile())); 161} 162 163OpaqueBrowserFrameView::~OpaqueBrowserFrameView() { 164} 165 166/////////////////////////////////////////////////////////////////////////////// 167// OpaqueBrowserFrameView, BrowserNonClientFrameView implementation: 168 169gfx::Rect OpaqueBrowserFrameView::GetBoundsForTabStrip( 170 views::View* tabstrip) const { 171 if (!tabstrip) 172 return gfx::Rect(); 173 174 return layout_->GetBoundsForTabStrip(tabstrip->GetPreferredSize(), width()); 175} 176 177int OpaqueBrowserFrameView::GetTopInset() const { 178 return browser_view()->IsTabStripVisible() ? 179 layout_->GetTabStripInsetsTop(false) : 180 layout_->NonClientTopBorderHeight(false); 181} 182 183int OpaqueBrowserFrameView::GetThemeBackgroundXInset() const { 184 return 0; 185} 186 187void OpaqueBrowserFrameView::UpdateThrobber(bool running) { 188 if (window_icon_) 189 window_icon_->Update(); 190} 191 192gfx::Size OpaqueBrowserFrameView::GetMinimumSize() const { 193 return layout_->GetMinimumSize(width()); 194} 195 196/////////////////////////////////////////////////////////////////////////////// 197// OpaqueBrowserFrameView, views::NonClientFrameView implementation: 198 199gfx::Rect OpaqueBrowserFrameView::GetBoundsForClientView() const { 200 return layout_->client_view_bounds(); 201} 202 203gfx::Rect OpaqueBrowserFrameView::GetWindowBoundsForClientBounds( 204 const gfx::Rect& client_bounds) const { 205 return layout_->GetWindowBoundsForClientBounds(client_bounds); 206} 207 208int OpaqueBrowserFrameView::NonClientHitTest(const gfx::Point& point) { 209 if (!bounds().Contains(point)) 210 return HTNOWHERE; 211 212 // See if the point is within the avatar menu button or within the avatar 213 // label. 214 if ((avatar_button() && 215 avatar_button()->GetMirroredBounds().Contains(point)) || 216 (avatar_label() && avatar_label()->GetMirroredBounds().Contains(point)) || 217 (new_avatar_button() && 218 new_avatar_button()->GetMirroredBounds().Contains(point))) 219 return HTCLIENT; 220 221 int frame_component = frame()->client_view()->NonClientHitTest(point); 222 223 // See if we're in the sysmenu region. We still have to check the tabstrip 224 // first so that clicks in a tab don't get treated as sysmenu clicks. 225 gfx::Rect sysmenu_rect(IconBounds()); 226 // In maximized mode we extend the rect to the screen corner to take advantage 227 // of Fitts' Law. 228 if (layout_->IsTitleBarCondensed()) 229 sysmenu_rect.SetRect(0, 0, sysmenu_rect.right(), sysmenu_rect.bottom()); 230 sysmenu_rect.set_x(GetMirroredXForRect(sysmenu_rect)); 231 if (sysmenu_rect.Contains(point)) 232 return (frame_component == HTCLIENT) ? HTCLIENT : HTSYSMENU; 233 234 if (frame_component != HTNOWHERE) 235 return frame_component; 236 237 // Then see if the point is within any of the window controls. 238 if (close_button_ && close_button_->visible() && 239 close_button_->GetMirroredBounds().Contains(point)) 240 return HTCLOSE; 241 if (restore_button_ && restore_button_->visible() && 242 restore_button_->GetMirroredBounds().Contains(point)) 243 return HTMAXBUTTON; 244 if (maximize_button_ && maximize_button_->visible() && 245 maximize_button_->GetMirroredBounds().Contains(point)) 246 return HTMAXBUTTON; 247 if (minimize_button_ && minimize_button_->visible() && 248 minimize_button_->GetMirroredBounds().Contains(point)) 249 return HTMINBUTTON; 250 251 views::WidgetDelegate* delegate = frame()->widget_delegate(); 252 if (!delegate) { 253 LOG(WARNING) << "delegate is NULL, returning safe default."; 254 return HTCAPTION; 255 } 256 int window_component = GetHTComponentForFrame(point, TopResizeHeight(), 257 NonClientBorderThickness(), kResizeAreaCornerSize, kResizeAreaCornerSize, 258 delegate->CanResize()); 259 // Fall back to the caption if no other component matches. 260 return (window_component == HTNOWHERE) ? HTCAPTION : window_component; 261} 262 263void OpaqueBrowserFrameView::GetWindowMask(const gfx::Size& size, 264 gfx::Path* window_mask) { 265 DCHECK(window_mask); 266 267 if (layout_->IsTitleBarCondensed() || frame()->IsFullscreen()) 268 return; 269 270 views::GetDefaultWindowMask(size, window_mask); 271} 272 273void OpaqueBrowserFrameView::ResetWindowControls() { 274 restore_button_->SetState(views::CustomButton::STATE_NORMAL); 275 minimize_button_->SetState(views::CustomButton::STATE_NORMAL); 276 maximize_button_->SetState(views::CustomButton::STATE_NORMAL); 277 // The close button isn't affected by this constraint. 278} 279 280void OpaqueBrowserFrameView::UpdateWindowIcon() { 281 window_icon_->SchedulePaint(); 282} 283 284void OpaqueBrowserFrameView::UpdateWindowTitle() { 285 if (!frame()->IsFullscreen()) 286 window_title_->SchedulePaint(); 287} 288 289/////////////////////////////////////////////////////////////////////////////// 290// OpaqueBrowserFrameView, views::View overrides: 291 292void OpaqueBrowserFrameView::GetAccessibleState( 293 ui::AXViewState* state) { 294 state->role = ui::AX_ROLE_TITLE_BAR; 295} 296 297/////////////////////////////////////////////////////////////////////////////// 298// OpaqueBrowserFrameView, views::ButtonListener implementation: 299 300void OpaqueBrowserFrameView::ButtonPressed(views::Button* sender, 301 const ui::Event& event) { 302 if (sender == minimize_button_) { 303 frame()->Minimize(); 304 } else if (sender == maximize_button_) { 305 frame()->Maximize(); 306 } else if (sender == restore_button_) { 307 frame()->Restore(); 308 } else if (sender == close_button_) { 309 frame()->Close(); 310 } else if (sender == new_avatar_button()) { 311 browser_view()->ShowAvatarBubbleFromAvatarButton( 312 BrowserWindow::AVATAR_BUBBLE_MODE_DEFAULT, 313 signin::ManageAccountsParams()); 314 } 315} 316 317void OpaqueBrowserFrameView::OnMenuButtonClicked(views::View* source, 318 const gfx::Point& point) { 319#if defined(OS_LINUX) 320 views::MenuRunner menu_runner(frame()->GetSystemMenuModel(), 321 views::MenuRunner::HAS_MNEMONICS); 322 ignore_result(menu_runner.RunMenuAt(browser_view()->GetWidget(), 323 window_icon_, 324 window_icon_->GetBoundsInScreen(), 325 views::MENU_ANCHOR_TOPLEFT, 326 ui::MENU_SOURCE_MOUSE)); 327#endif 328} 329 330/////////////////////////////////////////////////////////////////////////////// 331// OpaqueBrowserFrameView, TabIconView::TabContentsProvider implementation: 332 333bool OpaqueBrowserFrameView::ShouldTabIconViewAnimate() const { 334 // This function is queried during the creation of the window as the 335 // TabIconView we host is initialized, so we need to NULL check the selected 336 // WebContents because in this condition there is not yet a selected tab. 337 WebContents* current_tab = browser_view()->GetActiveWebContents(); 338 return current_tab ? current_tab->IsLoading() : false; 339} 340 341gfx::ImageSkia OpaqueBrowserFrameView::GetFaviconForTabIconView() { 342 views::WidgetDelegate* delegate = frame()->widget_delegate(); 343 if (!delegate) { 344 LOG(WARNING) << "delegate is NULL, returning safe default."; 345 return gfx::ImageSkia(); 346 } 347 return delegate->GetWindowIcon(); 348} 349 350/////////////////////////////////////////////////////////////////////////////// 351// OpaqueBrowserFrameView, protected: 352 353void OpaqueBrowserFrameView::Observe( 354 int type, 355 const content::NotificationSource& source, 356 const content::NotificationDetails& details) { 357 switch (type) { 358 case chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED: 359 if (browser_view() ->IsRegularOrGuestSession() && 360 switches::IsNewAvatarMenu()) { 361 UpdateNewStyleAvatarInfo(this, NewAvatarButton::THEMED_BUTTON); 362 } else { 363 UpdateAvatarInfo(); 364 } 365 break; 366 default: 367 NOTREACHED() << "Got a notification we didn't register for!"; 368 break; 369 } 370} 371 372/////////////////////////////////////////////////////////////////////////////// 373// OpaqueBrowserFrameView, OpaqueBrowserFrameViewLayoutDelegate implementation: 374 375bool OpaqueBrowserFrameView::ShouldShowWindowIcon() const { 376 views::WidgetDelegate* delegate = frame()->widget_delegate(); 377 return ShouldShowWindowTitleBar() && delegate && 378 delegate->ShouldShowWindowIcon(); 379} 380 381bool OpaqueBrowserFrameView::ShouldShowWindowTitle() const { 382 // |delegate| may be NULL if called from callback of InputMethodChanged while 383 // a window is being destroyed. 384 // See more discussion at http://crosbug.com/8958 385 views::WidgetDelegate* delegate = frame()->widget_delegate(); 386 return ShouldShowWindowTitleBar() && delegate && 387 delegate->ShouldShowWindowTitle(); 388} 389 390base::string16 OpaqueBrowserFrameView::GetWindowTitle() const { 391 return frame()->widget_delegate()->GetWindowTitle(); 392} 393 394int OpaqueBrowserFrameView::GetIconSize() const { 395#if defined(OS_WIN) 396 // This metric scales up if either the titlebar height or the titlebar font 397 // size are increased. 398 return GetSystemMetrics(SM_CYSMICON); 399#else 400 return std::max(BrowserFrame::GetTitleFontList().GetHeight(), 401 kIconMinimumSize); 402#endif 403} 404 405bool OpaqueBrowserFrameView::ShouldLeaveOffsetNearTopBorder() const { 406 return frame()->ShouldLeaveOffsetNearTopBorder(); 407} 408 409gfx::Size OpaqueBrowserFrameView::GetBrowserViewMinimumSize() const { 410 return browser_view()->GetMinimumSize(); 411} 412 413bool OpaqueBrowserFrameView::ShouldShowCaptionButtons() const { 414 return ShouldShowWindowTitleBar(); 415} 416 417bool OpaqueBrowserFrameView::ShouldShowAvatar() const { 418 return browser_view()->ShouldShowAvatar(); 419} 420 421bool OpaqueBrowserFrameView::IsRegularOrGuestSession() const { 422 return browser_view()->IsRegularOrGuestSession(); 423} 424 425gfx::ImageSkia OpaqueBrowserFrameView::GetOTRAvatarIcon() const { 426 return browser_view()->GetOTRAvatarIcon(); 427} 428 429bool OpaqueBrowserFrameView::IsMaximized() const { 430 return frame()->IsMaximized(); 431} 432 433bool OpaqueBrowserFrameView::IsMinimized() const { 434 return frame()->IsMinimized(); 435} 436 437bool OpaqueBrowserFrameView::IsFullscreen() const { 438 return frame()->IsFullscreen(); 439} 440 441bool OpaqueBrowserFrameView::IsTabStripVisible() const { 442 return browser_view()->IsTabStripVisible(); 443} 444 445int OpaqueBrowserFrameView::GetTabStripHeight() const { 446 return browser_view()->GetTabStripHeight(); 447} 448 449gfx::Size OpaqueBrowserFrameView::GetTabstripPreferredSize() const { 450 gfx::Size s = browser_view()->tabstrip()->GetPreferredSize(); 451 return s; 452} 453 454/////////////////////////////////////////////////////////////////////////////// 455// OpaqueBrowserFrameView, views::View overrides: 456 457void OpaqueBrowserFrameView::OnPaint(gfx::Canvas* canvas) { 458 if (frame()->IsFullscreen()) 459 return; // Nothing is visible, so don't bother to paint. 460 461 if (layout_->IsTitleBarCondensed()) 462 PaintMaximizedFrameBorder(canvas); 463 else 464 PaintRestoredFrameBorder(canvas); 465 466 // The window icon and title are painted by their respective views. 467 /* TODO(pkasting): If this window is active, we should also draw a drop 468 * shadow on the title. This is tricky, because we don't want to hardcode a 469 * shadow color (since we want to work with various themes), but we can't 470 * alpha-blend either (since the Windows text APIs don't really do this). 471 * So we'd need to sample the background color at the right location and 472 * synthesize a good shadow color. */ 473 474 if (browser_view()->IsToolbarVisible()) 475 PaintToolbarBackground(canvas); 476 if (!layout_->IsTitleBarCondensed()) 477 PaintRestoredClientEdge(canvas); 478} 479 480/////////////////////////////////////////////////////////////////////////////// 481// OpaqueBrowserFrameView, private: 482 483// views::NonClientFrameView: 484bool OpaqueBrowserFrameView::DoesIntersectRect(const views::View* target, 485 const gfx::Rect& rect) const { 486 CHECK_EQ(target, this); 487 if (!views::ViewTargeterDelegate::DoesIntersectRect(this, rect)) { 488 // |rect| is outside OpaqueBrowserFrameView's bounds. 489 return false; 490 } 491 492 // If the rect is outside the bounds of the client area, claim it. 493 gfx::RectF rect_in_client_view_coords_f(rect); 494 View::ConvertRectToTarget(this, frame()->client_view(), 495 &rect_in_client_view_coords_f); 496 gfx::Rect rect_in_client_view_coords = gfx::ToEnclosingRect( 497 rect_in_client_view_coords_f); 498 if (!frame()->client_view()->HitTestRect(rect_in_client_view_coords)) 499 return true; 500 501 // Otherwise, claim |rect| only if it is above the bottom of the tabstrip in 502 // a non-tab portion. 503 TabStrip* tabstrip = browser_view()->tabstrip(); 504 if (!tabstrip || !browser_view()->IsTabStripVisible()) 505 return false; 506 507 gfx::RectF rect_in_tabstrip_coords_f(rect); 508 View::ConvertRectToTarget(this, tabstrip, &rect_in_tabstrip_coords_f); 509 gfx::Rect rect_in_tabstrip_coords = gfx::ToEnclosingRect( 510 rect_in_tabstrip_coords_f); 511 if (rect_in_tabstrip_coords.bottom() > tabstrip->GetLocalBounds().bottom()) { 512 // |rect| is below the tabstrip. 513 return false; 514 } 515 516 if (tabstrip->HitTestRect(rect_in_tabstrip_coords)) { 517 // Claim |rect| if it is in a non-tab portion of the tabstrip. 518 return tabstrip->IsRectInWindowCaption(rect_in_tabstrip_coords); 519 } 520 521 // We claim |rect| because it is above the bottom of the tabstrip, but 522 // not in the tabstrip itself. In particular, the avatar label/button is left 523 // of the tabstrip and the window controls are right of the tabstrip. 524 return true; 525} 526 527views::ImageButton* OpaqueBrowserFrameView::InitWindowCaptionButton( 528 int normal_image_id, 529 int hot_image_id, 530 int pushed_image_id, 531 int mask_image_id, 532 int accessibility_string_id, 533 ViewID view_id) { 534 views::ImageButton* button = new views::ImageButton(this); 535 ui::ThemeProvider* tp = frame()->GetThemeProvider(); 536 button->SetImage(views::CustomButton::STATE_NORMAL, 537 tp->GetImageSkiaNamed(normal_image_id)); 538 button->SetImage(views::CustomButton::STATE_HOVERED, 539 tp->GetImageSkiaNamed(hot_image_id)); 540 button->SetImage(views::CustomButton::STATE_PRESSED, 541 tp->GetImageSkiaNamed(pushed_image_id)); 542 if (browser_view()->IsBrowserTypeNormal()) { 543 button->SetBackground( 544 tp->GetColor(ThemeProperties::COLOR_BUTTON_BACKGROUND), 545 tp->GetImageSkiaNamed(IDR_THEME_WINDOW_CONTROL_BACKGROUND), 546 tp->GetImageSkiaNamed(mask_image_id)); 547 } 548 button->SetAccessibleName( 549 l10n_util::GetStringUTF16(accessibility_string_id)); 550 button->set_id(view_id); 551 AddChildView(button); 552 return button; 553} 554 555int OpaqueBrowserFrameView::FrameBorderThickness(bool restored) const { 556 return layout_->FrameBorderThickness(restored); 557} 558 559int OpaqueBrowserFrameView::TopResizeHeight() const { 560 return FrameBorderThickness(false) - kTopResizeAdjust; 561} 562 563int OpaqueBrowserFrameView::NonClientBorderThickness() const { 564 return layout_->NonClientBorderThickness(); 565} 566 567gfx::Rect OpaqueBrowserFrameView::IconBounds() const { 568 return layout_->IconBounds(); 569} 570 571bool OpaqueBrowserFrameView::ShouldShowWindowTitleBar() const { 572#if defined(OS_LINUX) && !defined(OS_CHROMEOS) 573 // Do not show the custom title bar if the system title bar option is enabled. 574 if (!frame()->UseCustomFrame()) 575 return false; 576#endif 577 578 // Do not show caption buttons if the window manager is forcefully providing a 579 // title bar (e.g., in Ubuntu Unity, if the window is maximized). 580 if (!views::ViewsDelegate::views_delegate) 581 return true; 582 return !views::ViewsDelegate::views_delegate->WindowManagerProvidesTitleBar( 583 IsMaximized()); 584} 585 586void OpaqueBrowserFrameView::PaintRestoredFrameBorder(gfx::Canvas* canvas) { 587 frame_background_->set_frame_color(GetFrameColor()); 588 frame_background_->set_theme_image(GetFrameImage()); 589 frame_background_->set_theme_overlay_image(GetFrameOverlayImage()); 590 frame_background_->set_top_area_height(GetTopAreaHeight()); 591 592 ui::ThemeProvider* tp = GetThemeProvider(); 593 frame_background_->SetSideImages( 594 tp->GetImageSkiaNamed(IDR_WINDOW_LEFT_SIDE), 595 tp->GetImageSkiaNamed(IDR_WINDOW_TOP_CENTER), 596 tp->GetImageSkiaNamed(IDR_WINDOW_RIGHT_SIDE), 597 tp->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_CENTER)); 598 frame_background_->SetCornerImages( 599 tp->GetImageSkiaNamed(IDR_WINDOW_TOP_LEFT_CORNER), 600 tp->GetImageSkiaNamed(IDR_WINDOW_TOP_RIGHT_CORNER), 601 tp->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_LEFT_CORNER), 602 tp->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_RIGHT_CORNER)); 603 frame_background_->PaintRestored(canvas, this); 604 605 // Note: When we don't have a toolbar, we need to draw some kind of bottom 606 // edge here. Because the App Window graphics we use for this have an 607 // attached client edge and their sizing algorithm is a little involved, we do 608 // all this in PaintRestoredClientEdge(). 609} 610 611void OpaqueBrowserFrameView::PaintMaximizedFrameBorder(gfx::Canvas* canvas) { 612 ui::ThemeProvider* tp = GetThemeProvider(); 613 frame_background_->set_frame_color(GetFrameColor()); 614 frame_background_->set_theme_image(GetFrameImage()); 615 frame_background_->set_theme_overlay_image(GetFrameOverlayImage()); 616 frame_background_->set_top_area_height(GetTopAreaHeight()); 617#if defined(OS_LINUX) && !defined(OS_CHROMEOS) 618 // The window manager typically shows a gradient in the native title bar (when 619 // the system title bar pref is set, or when maximized on Ubuntu). Hide the 620 // gradient in the tab strip (by shifting it up vertically) to avoid a 621 // double-gradient effect. 622 if (tp->UsingSystemTheme()) 623 frame_background_->set_maximized_top_inset(kGTKThemeCondensedFrameTopInset); 624#endif 625 626 frame_background_->PaintMaximized(canvas, this); 627 628 // TODO(jamescook): Migrate this into FrameBackground. 629 if (!browser_view()->IsToolbarVisible()) { 630 // There's no toolbar to edge the frame border, so we need to draw a bottom 631 // edge. The graphic we use for this has a built in client edge, so we clip 632 // it off the bottom. 633 gfx::ImageSkia* top_center = tp->GetImageSkiaNamed(IDR_APP_TOP_CENTER); 634 int edge_height = top_center->height() - kClientEdgeThickness; 635 canvas->TileImageInt(*top_center, 0, 636 frame()->client_view()->y() - edge_height, width(), edge_height); 637 } 638} 639 640void OpaqueBrowserFrameView::PaintToolbarBackground(gfx::Canvas* canvas) { 641 gfx::Rect toolbar_bounds(browser_view()->GetToolbarBounds()); 642 if (toolbar_bounds.IsEmpty()) 643 return; 644 gfx::Point toolbar_origin(toolbar_bounds.origin()); 645 ConvertPointToTarget(browser_view(), this, &toolbar_origin); 646 toolbar_bounds.set_origin(toolbar_origin); 647 648 int x = toolbar_bounds.x(); 649 int w = toolbar_bounds.width(); 650 int y = toolbar_bounds.y(); 651 int h = toolbar_bounds.height(); 652 653 // Gross hack: We split the toolbar images into two pieces, since sometimes 654 // (popup mode) the toolbar isn't tall enough to show the whole image. The 655 // split happens between the top shadow section and the bottom gradient 656 // section so that we never break the gradient. 657 int split_point = kFrameShadowThickness * 2; 658 int bottom_y = y + split_point; 659 ui::ThemeProvider* tp = GetThemeProvider(); 660 gfx::ImageSkia* toolbar_left = tp->GetImageSkiaNamed( 661 IDR_CONTENT_TOP_LEFT_CORNER); 662 int bottom_edge_height = std::min(toolbar_left->height(), h) - split_point; 663 664 // Split our canvas out so we can mask out the corners of the toolbar 665 // without masking out the frame. 666 canvas->SaveLayerAlpha( 667 255, gfx::Rect(x - kClientEdgeThickness, y, w + kClientEdgeThickness * 3, 668 h)); 669 670 // Paint the bottom rect. 671 canvas->FillRect(gfx::Rect(x, bottom_y, w, bottom_edge_height), 672 tp->GetColor(ThemeProperties::COLOR_TOOLBAR)); 673 674 // Tile the toolbar image starting at the frame edge on the left and where the 675 // horizontal tabstrip is (or would be) on the top. 676 gfx::ImageSkia* theme_toolbar = tp->GetImageSkiaNamed(IDR_THEME_TOOLBAR); 677 canvas->TileImageInt(*theme_toolbar, 678 x + GetThemeBackgroundXInset(), 679 bottom_y - GetTopInset(), 680 x, bottom_y, w, theme_toolbar->height()); 681 682 // Draw rounded corners for the tab. 683 gfx::ImageSkia* toolbar_left_mask = 684 tp->GetImageSkiaNamed(IDR_CONTENT_TOP_LEFT_CORNER_MASK); 685 gfx::ImageSkia* toolbar_right_mask = 686 tp->GetImageSkiaNamed(IDR_CONTENT_TOP_RIGHT_CORNER_MASK); 687 688 // We mask out the corners by using the DestinationIn transfer mode, 689 // which keeps the RGB pixels from the destination and the alpha from 690 // the source. 691 SkPaint paint; 692 paint.setXfermodeMode(SkXfermode::kDstIn_Mode); 693 694 // Mask the left edge. 695 int left_x = x - kContentEdgeShadowThickness; 696 canvas->DrawImageInt(*toolbar_left_mask, 0, 0, toolbar_left_mask->width(), 697 split_point, left_x, y, toolbar_left_mask->width(), 698 split_point, false, paint); 699 canvas->DrawImageInt(*toolbar_left_mask, 0, 700 toolbar_left_mask->height() - bottom_edge_height, 701 toolbar_left_mask->width(), bottom_edge_height, left_x, bottom_y, 702 toolbar_left_mask->width(), bottom_edge_height, false, paint); 703 704 // Mask the right edge. 705 int right_x = 706 x + w - toolbar_right_mask->width() + kContentEdgeShadowThickness; 707 canvas->DrawImageInt(*toolbar_right_mask, 0, 0, toolbar_right_mask->width(), 708 split_point, right_x, y, toolbar_right_mask->width(), 709 split_point, false, paint); 710 canvas->DrawImageInt(*toolbar_right_mask, 0, 711 toolbar_right_mask->height() - bottom_edge_height, 712 toolbar_right_mask->width(), bottom_edge_height, right_x, bottom_y, 713 toolbar_right_mask->width(), bottom_edge_height, false, paint); 714 canvas->Restore(); 715 716 canvas->DrawImageInt(*toolbar_left, 0, 0, toolbar_left->width(), split_point, 717 left_x, y, toolbar_left->width(), split_point, false); 718 canvas->DrawImageInt(*toolbar_left, 0, 719 toolbar_left->height() - bottom_edge_height, toolbar_left->width(), 720 bottom_edge_height, left_x, bottom_y, toolbar_left->width(), 721 bottom_edge_height, false); 722 723 gfx::ImageSkia* toolbar_center = 724 tp->GetImageSkiaNamed(IDR_CONTENT_TOP_CENTER); 725 canvas->TileImageInt(*toolbar_center, 0, 0, left_x + toolbar_left->width(), 726 y, right_x - (left_x + toolbar_left->width()), 727 split_point); 728 729 gfx::ImageSkia* toolbar_right = tp->GetImageSkiaNamed( 730 IDR_CONTENT_TOP_RIGHT_CORNER); 731 canvas->DrawImageInt(*toolbar_right, 0, 0, toolbar_right->width(), 732 split_point, right_x, y, toolbar_right->width(), split_point, false); 733 canvas->DrawImageInt(*toolbar_right, 0, 734 toolbar_right->height() - bottom_edge_height, toolbar_right->width(), 735 bottom_edge_height, right_x, bottom_y, toolbar_right->width(), 736 bottom_edge_height, false); 737 738 // Draw the content/toolbar separator. 739 canvas->FillRect( 740 gfx::Rect(x + kClientEdgeThickness, 741 toolbar_bounds.bottom() - kClientEdgeThickness, 742 w - (2 * kClientEdgeThickness), 743 kClientEdgeThickness), 744 ThemeProperties::GetDefaultColor( 745 ThemeProperties::COLOR_TOOLBAR_SEPARATOR)); 746} 747 748void OpaqueBrowserFrameView::PaintRestoredClientEdge(gfx::Canvas* canvas) { 749 ui::ThemeProvider* tp = GetThemeProvider(); 750 int client_area_top = frame()->client_view()->y(); 751 int image_top = client_area_top; 752 753 gfx::Rect client_area_bounds = 754 layout_->CalculateClientAreaBounds(width(), height()); 755 SkColor toolbar_color = tp->GetColor(ThemeProperties::COLOR_TOOLBAR); 756 757 if (browser_view()->IsToolbarVisible()) { 758 // The client edge images always start below the toolbar corner images. The 759 // client edge filled rects start there or at the bottom of the toolbar, 760 // whichever is shorter. 761 gfx::Rect toolbar_bounds(browser_view()->GetToolbarBounds()); 762 763 gfx::ImageSkia* content_top_left_corner = 764 tp->GetImageSkiaNamed(IDR_CONTENT_TOP_LEFT_CORNER); 765 // TODO(oshima): Sanity checks for crbug.com/374273. Remove when it's fixed. 766 CHECK(content_top_left_corner); 767 CHECK(!content_top_left_corner->isNull()); 768 769 image_top += toolbar_bounds.y() + content_top_left_corner->height(); 770 client_area_top = std::min(image_top, 771 client_area_top + toolbar_bounds.bottom() - kClientEdgeThickness); 772 } else if (!browser_view()->IsTabStripVisible()) { 773 // The toolbar isn't going to draw a client edge for us, so draw one 774 // ourselves. 775 gfx::ImageSkia* top_left = tp->GetImageSkiaNamed(IDR_APP_TOP_LEFT); 776 gfx::ImageSkia* top_center = tp->GetImageSkiaNamed(IDR_APP_TOP_CENTER); 777 gfx::ImageSkia* top_right = tp->GetImageSkiaNamed(IDR_APP_TOP_RIGHT); 778 int top_edge_y = client_area_top - top_center->height(); 779 int height = client_area_top - top_edge_y; 780 781 canvas->DrawImageInt(*top_left, 0, 0, top_left->width(), height, 782 client_area_bounds.x() - top_left->width(), top_edge_y, 783 top_left->width(), height, false); 784 canvas->TileImageInt(*top_center, 0, 0, client_area_bounds.x(), top_edge_y, 785 client_area_bounds.width(), std::min(height, top_center->height())); 786 canvas->DrawImageInt(*top_right, 0, 0, top_right->width(), height, 787 client_area_bounds.right(), top_edge_y, 788 top_right->width(), height, false); 789 790 // Draw the toolbar color across the top edge. 791 canvas->FillRect(gfx::Rect(client_area_bounds.x() - kClientEdgeThickness, 792 client_area_top - kClientEdgeThickness, 793 client_area_bounds.width() + (2 * kClientEdgeThickness), 794 kClientEdgeThickness), toolbar_color); 795 } 796 797 int client_area_bottom = 798 std::max(client_area_top, height() - NonClientBorderThickness()); 799 int image_height = client_area_bottom - image_top; 800 801 // Draw the client edge images. 802 gfx::ImageSkia* right = tp->GetImageSkiaNamed(IDR_CONTENT_RIGHT_SIDE); 803 canvas->TileImageInt(*right, client_area_bounds.right(), image_top, 804 right->width(), image_height); 805 canvas->DrawImageInt( 806 *tp->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_RIGHT_CORNER), 807 client_area_bounds.right(), client_area_bottom); 808 gfx::ImageSkia* bottom = tp->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_CENTER); 809 canvas->TileImageInt(*bottom, client_area_bounds.x(), 810 client_area_bottom, client_area_bounds.width(), 811 bottom->height()); 812 gfx::ImageSkia* bottom_left = 813 tp->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_LEFT_CORNER); 814 canvas->DrawImageInt(*bottom_left, 815 client_area_bounds.x() - bottom_left->width(), client_area_bottom); 816 gfx::ImageSkia* left = tp->GetImageSkiaNamed(IDR_CONTENT_LEFT_SIDE); 817 canvas->TileImageInt(*left, client_area_bounds.x() - left->width(), 818 image_top, left->width(), image_height); 819 820 // Draw the toolbar color so that the client edges show the right color even 821 // where not covered by the toolbar image. NOTE: We do this after drawing the 822 // images because the images are meant to alpha-blend atop the frame whereas 823 // these rects are meant to be fully opaque, without anything overlaid. 824 canvas->FillRect(gfx::Rect(client_area_bounds.x() - kClientEdgeThickness, 825 client_area_top, kClientEdgeThickness, 826 client_area_bottom + kClientEdgeThickness - client_area_top), 827 toolbar_color); 828 canvas->FillRect(gfx::Rect(client_area_bounds.x(), client_area_bottom, 829 client_area_bounds.width(), kClientEdgeThickness), 830 toolbar_color); 831 canvas->FillRect(gfx::Rect(client_area_bounds.right(), client_area_top, 832 kClientEdgeThickness, 833 client_area_bottom + kClientEdgeThickness - client_area_top), 834 toolbar_color); 835} 836 837SkColor OpaqueBrowserFrameView::GetFrameColor() const { 838 bool is_incognito = browser_view()->IsOffTheRecord(); 839 ThemeProperties::OverwritableByUserThemeProperty color_id; 840 if (ShouldPaintAsActive()) { 841 color_id = is_incognito ? 842 ThemeProperties::COLOR_FRAME_INCOGNITO : 843 ThemeProperties::COLOR_FRAME; 844 } else { 845 color_id = is_incognito ? 846 ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE : 847 ThemeProperties::COLOR_FRAME_INACTIVE; 848 } 849 850 if (browser_view()->IsBrowserTypeNormal() || 851 platform_observer_->IsUsingSystemTheme()) { 852 return GetThemeProvider()->GetColor(color_id); 853 } 854 855 // Never theme app and popup windows unless the |platform_observer_| 856 // requested an override. 857 return ThemeProperties::GetDefaultColor(color_id); 858} 859 860gfx::ImageSkia* OpaqueBrowserFrameView::GetFrameImage() const { 861 bool is_incognito = browser_view()->IsOffTheRecord(); 862 int resource_id; 863 if (browser_view()->IsBrowserTypeNormal()) { 864 if (ShouldPaintAsActive()) { 865 resource_id = is_incognito ? 866 IDR_THEME_FRAME_INCOGNITO : IDR_THEME_FRAME; 867 } else { 868 resource_id = is_incognito ? 869 IDR_THEME_FRAME_INCOGNITO_INACTIVE : IDR_THEME_FRAME_INACTIVE; 870 } 871 return GetThemeProvider()->GetImageSkiaNamed(resource_id); 872 } 873 if (ShouldPaintAsActive()) { 874 resource_id = is_incognito ? 875 IDR_THEME_FRAME_INCOGNITO : IDR_FRAME; 876 } else { 877 resource_id = is_incognito ? 878 IDR_THEME_FRAME_INCOGNITO_INACTIVE : IDR_THEME_FRAME_INACTIVE; 879 } 880 881 if (platform_observer_->IsUsingSystemTheme()) { 882 // We want to use theme images provided by the system theme when enabled, 883 // even if we are an app or popup window. 884 return GetThemeProvider()->GetImageSkiaNamed(resource_id); 885 } 886 887 // Otherwise, never theme app and popup windows. 888 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 889 return rb.GetImageSkiaNamed(chrome::MapThemeImage( 890 chrome::GetHostDesktopTypeForNativeWindow( 891 browser_view()->GetNativeWindow()), 892 resource_id)); 893} 894 895gfx::ImageSkia* OpaqueBrowserFrameView::GetFrameOverlayImage() const { 896 ui::ThemeProvider* tp = GetThemeProvider(); 897 if (tp->HasCustomImage(IDR_THEME_FRAME_OVERLAY) && 898 browser_view()->IsBrowserTypeNormal() && 899 !browser_view()->IsOffTheRecord()) { 900 return tp->GetImageSkiaNamed(ShouldPaintAsActive() ? 901 IDR_THEME_FRAME_OVERLAY : IDR_THEME_FRAME_OVERLAY_INACTIVE); 902 } 903 return NULL; 904} 905 906int OpaqueBrowserFrameView::GetTopAreaHeight() const { 907 gfx::ImageSkia* frame_image = GetFrameImage(); 908 int top_area_height = frame_image->height(); 909 if (browser_view()->IsTabStripVisible()) { 910 top_area_height = std::max(top_area_height, 911 GetBoundsForTabStrip(browser_view()->tabstrip()).bottom()); 912 } 913 return top_area_height; 914} 915