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