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