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/command_line.h" 11 #include "base/compiler_specific.h" 12 #include "base/prefs/pref_service.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "chrome/browser/chrome_notification_types.h" 15 #include "chrome/browser/themes/theme_properties.h" 16 #include "chrome/browser/ui/views/avatar_label.h" 17 #include "chrome/browser/ui/views/avatar_menu_button.h" 18 #include "chrome/browser/ui/views/frame/browser_frame.h" 19 #include "chrome/browser/ui/views/frame/browser_view.h" 20 #include "chrome/browser/ui/views/tab_icon_view.h" 21 #include "chrome/browser/ui/views/tabs/tab_strip.h" 22 #include "chrome/browser/ui/views/toolbar_view.h" 23 #include "chrome/common/chrome_switches.h" 24 #include "chrome/common/pref_names.h" 25 #include "content/public/browser/notification_service.h" 26 #include "content/public/browser/web_contents.h" 27 #include "grit/chromium_strings.h" 28 #include "grit/generated_resources.h" 29 #include "grit/theme_resources.h" 30 #include "grit/ui_resources.h" 31 #include "ui/base/accessibility/accessible_view_state.h" 32 #include "ui/base/hit_test.h" 33 #include "ui/base/l10n/l10n_util.h" 34 #include "ui/base/resource/resource_bundle.h" 35 #include "ui/base/theme_provider.h" 36 #include "ui/gfx/canvas.h" 37 #include "ui/gfx/font.h" 38 #include "ui/gfx/image/image.h" 39 #include "ui/gfx/image/image_skia.h" 40 #include "ui/gfx/path.h" 41 #include "ui/views/controls/button/image_button.h" 42 #include "ui/views/controls/image_view.h" 43 #include "ui/views/controls/label.h" 44 #include "ui/views/layout/layout_constants.h" 45 #include "ui/views/widget/root_view.h" 46 #include "ui/views/window/frame_background.h" 47 #include "ui/views/window/window_shape.h" 48 49 #if defined(OS_WIN) 50 #include "win8/util/win8_util.h" 51 #endif // OS_WIN 52 53 using content::WebContents; 54 55 namespace { 56 57 // The frame border is only visible in restored mode and is hardcoded to 4 px on 58 // each side regardless of the system window border size. 59 const int kFrameBorderThickness = 4; 60 // Besides the frame border, there's another 9 px of empty space atop the 61 // window in restored mode, to use to drag the window around. 62 const int kNonClientRestoredExtraThickness = 9; 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.) 68 const int kTopResizeAdjust = 1; 69 // In the window corners, the resize areas don't actually expand bigger, but the 70 // 16 px at the end of each edge triggers diagonal resizing. 71 const int kResizeAreaCornerSize = 16; 72 // The titlebar never shrinks too short to show the caption button plus some 73 // padding below it. 74 const int kCaptionButtonHeightWithPadding = 19; 75 // The content left/right images have a shadow built into them. 76 const int kContentEdgeShadowThickness = 2; 77 // The titlebar has a 2 px 3D edge along the top and bottom. 78 const int kTitlebarTopAndBottomEdgeThickness = 2; 79 // The icon is inset 2 px from the left frame border. 80 const int kIconLeftSpacing = 2; 81 // The icon never shrinks below 16 px on a side. 82 const int kIconMinimumSize = 16; 83 // There is a 4 px gap between the icon and the title text. 84 const int kIconTitleSpacing = 4; 85 // There is a 5 px gap between the title text and the caption buttons. 86 const int kTitleLogoSpacing = 5; 87 // The avatar ends 2 px above the bottom of the tabstrip (which, given the 88 // way the tabstrip draws its bottom edge, will appear like a 1 px gap to the 89 // user). 90 const int kAvatarBottomSpacing = 2; 91 // Space between the frame border and the left edge of the avatar. 92 const int kAvatarLeftSpacing = 2; 93 // Space between the right edge of the avatar and the tabstrip. 94 const int kAvatarRightSpacing = -2; 95 // The top 3 px of the tabstrip is shadow; in maximized mode we push this off 96 // the top of the screen so the tabs appear flush against the screen edge. 97 const int kTabstripTopShadowThickness = 3; 98 // In restored mode, the New Tab button isn't at the same height as the caption 99 // buttons, but the space will look cluttered if it actually slides under them, 100 // so we stop it when the gap between the two is down to 5 px. 101 const int kNewTabCaptionRestoredSpacing = 5; 102 // In maximized mode, where the New Tab button and the caption buttons are at 103 // similar vertical coordinates, we need to reserve a larger, 16 px gap to avoid 104 // looking too cluttered. 105 const int kNewTabCaptionMaximizedSpacing = 16; 106 // How far to indent the tabstrip from the left side of the screen when there 107 // is no avatar icon. 108 const int kTabStripIndent = -6; 109 110 // Converts |bounds| from |src|'s coordinate system to |dst|, and checks if 111 // |pt| is contained within. 112 bool ConvertedContainsCheck(gfx::Rect bounds, const views::View* src, 113 const views::View* dst, const gfx::Point& pt) { 114 DCHECK(src); 115 DCHECK(dst); 116 gfx::Point origin(bounds.origin()); 117 views::View::ConvertPointToTarget(src, dst, &origin); 118 bounds.set_origin(origin); 119 return bounds.Contains(pt); 120 } 121 122 bool ShouldAddDefaultCaptionButtons() { 123 #if defined(OS_WIN) 124 return !win8::IsSingleWindowMetroMode(); 125 #endif // OS_WIN 126 return true; 127 } 128 129 } // namespace 130 131 /////////////////////////////////////////////////////////////////////////////// 132 // OpaqueBrowserFrameView, public: 133 134 OpaqueBrowserFrameView::OpaqueBrowserFrameView(BrowserFrame* frame, 135 BrowserView* browser_view) 136 : BrowserNonClientFrameView(frame, browser_view), 137 minimize_button_(NULL), 138 maximize_button_(NULL), 139 restore_button_(NULL), 140 close_button_(NULL), 141 window_icon_(NULL), 142 window_title_(NULL), 143 frame_background_(new views::FrameBackground()) { 144 if (ShouldAddDefaultCaptionButtons()) { 145 minimize_button_ = InitWindowCaptionButton(IDR_MINIMIZE, 146 IDR_MINIMIZE_H, 147 IDR_MINIMIZE_P, 148 IDR_MINIMIZE_BUTTON_MASK, 149 IDS_ACCNAME_MINIMIZE); 150 maximize_button_ = InitWindowCaptionButton(IDR_MAXIMIZE, 151 IDR_MAXIMIZE_H, 152 IDR_MAXIMIZE_P, 153 IDR_MAXIMIZE_BUTTON_MASK, 154 IDS_ACCNAME_MAXIMIZE); 155 restore_button_ = InitWindowCaptionButton(IDR_RESTORE, 156 IDR_RESTORE_H, 157 IDR_RESTORE_P, 158 IDR_RESTORE_BUTTON_MASK, 159 IDS_ACCNAME_RESTORE); 160 close_button_ = InitWindowCaptionButton(IDR_CLOSE, 161 IDR_CLOSE_H, 162 IDR_CLOSE_P, 163 IDR_CLOSE_BUTTON_MASK, 164 IDS_ACCNAME_CLOSE); 165 } 166 167 // Initializing the TabIconView is expensive, so only do it if we need to. 168 if (browser_view->ShouldShowWindowIcon()) { 169 window_icon_ = new TabIconView(this); 170 window_icon_->set_is_light(true); 171 AddChildView(window_icon_); 172 window_icon_->Update(); 173 } 174 175 window_title_ = new views::Label(browser_view->GetWindowTitle(), 176 BrowserFrame::GetTitleFont()); 177 window_title_->SetVisible(browser_view->ShouldShowWindowTitle()); 178 window_title_->SetEnabledColor(SK_ColorWHITE); 179 // TODO(msw): Use a transparent background color as a workaround to use the 180 // gfx::Canvas::NO_SUBPIXEL_RENDERING flag and avoid some visual artifacts. 181 window_title_->SetBackgroundColor(0x00000000); 182 window_title_->SetHorizontalAlignment(gfx::ALIGN_LEFT); 183 AddChildView(window_title_); 184 185 UpdateAvatarInfo(); 186 if (!browser_view->IsOffTheRecord()) { 187 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, 188 content::NotificationService::AllSources()); 189 } 190 } 191 192 OpaqueBrowserFrameView::~OpaqueBrowserFrameView() { 193 } 194 195 /////////////////////////////////////////////////////////////////////////////// 196 // OpaqueBrowserFrameView, protected: 197 198 int OpaqueBrowserFrameView::GetReservedHeight() const { 199 return 0; 200 } 201 202 gfx::Rect OpaqueBrowserFrameView::GetBoundsForReservedArea() const { 203 gfx::Rect client_view_bounds = CalculateClientAreaBounds(width(), height()); 204 return gfx::Rect( 205 client_view_bounds.x(), 206 client_view_bounds.y() + client_view_bounds.height(), 207 client_view_bounds.width(), 208 GetReservedHeight()); 209 } 210 211 int OpaqueBrowserFrameView::NonClientTopBorderHeight( 212 bool restored) const { 213 views::WidgetDelegate* delegate = frame()->widget_delegate(); 214 // |delegate| may be NULL if called from callback of InputMethodChanged while 215 // a window is being destroyed. 216 // See more discussion at http://crosbug.com/8958 217 if (delegate && delegate->ShouldShowWindowTitle()) { 218 return std::max(FrameBorderThickness(restored) + IconSize(), 219 CaptionButtonY(restored) + kCaptionButtonHeightWithPadding) + 220 TitlebarBottomThickness(restored); 221 } 222 223 return FrameBorderThickness(restored) - 224 ((browser_view()->IsTabStripVisible() && 225 !restored && !frame()->ShouldLeaveOffsetNearTopBorder()) 226 ? kTabstripTopShadowThickness : 0); 227 } 228 229 /////////////////////////////////////////////////////////////////////////////// 230 // OpaqueBrowserFrameView, BrowserNonClientFrameView implementation: 231 232 gfx::Rect OpaqueBrowserFrameView::GetBoundsForTabStrip( 233 views::View* tabstrip) const { 234 if (!tabstrip) 235 return gfx::Rect(); 236 237 gfx::Rect bounds = GetBoundsForTabStripAndAvatarArea(tabstrip); 238 int space_left_of_tabstrip = kTabStripIndent; 239 if (browser_view()->ShouldShowAvatar()) { 240 if (avatar_label() && avatar_label()->bounds().width()) { 241 // Space between the right edge of the avatar label and the tabstrip. 242 const int kAvatarLabelRightSpacing = -10; 243 space_left_of_tabstrip = 244 avatar_label()->bounds().right() + kAvatarLabelRightSpacing; 245 } else { 246 space_left_of_tabstrip = 247 kAvatarLeftSpacing + avatar_bounds_.width() + kAvatarRightSpacing; 248 } 249 } 250 bounds.Inset(space_left_of_tabstrip, 0, 0, 0); 251 return bounds; 252 } 253 254 BrowserNonClientFrameView::TabStripInsets 255 OpaqueBrowserFrameView::GetTabStripInsets(bool restored) const { 256 int top = NonClientTopBorderHeight(restored) + ((!restored && 257 (!frame()->ShouldLeaveOffsetNearTopBorder() || 258 frame()->IsFullscreen())) ? 259 0 : kNonClientRestoredExtraThickness); 260 // TODO: include OTR and caption. 261 return TabStripInsets(top, 0, 0); 262 } 263 264 int OpaqueBrowserFrameView::GetThemeBackgroundXInset() const { 265 return 0; 266 } 267 268 void OpaqueBrowserFrameView::UpdateThrobber(bool running) { 269 if (window_icon_) 270 window_icon_->Update(); 271 } 272 273 gfx::Size OpaqueBrowserFrameView::GetMinimumSize() { 274 gfx::Size min_size(browser_view()->GetMinimumSize()); 275 int border_thickness = NonClientBorderThickness(); 276 min_size.Enlarge(2 * border_thickness, 277 NonClientTopBorderHeight(false) + border_thickness); 278 279 views::WidgetDelegate* delegate = frame()->widget_delegate(); 280 int min_titlebar_width = (2 * FrameBorderThickness(false)) + 281 kIconLeftSpacing + 282 (delegate && delegate->ShouldShowWindowIcon() ? 283 (IconSize() + kTitleLogoSpacing) : 0); 284 #if !defined(OS_CHROMEOS) 285 if (ShouldAddDefaultCaptionButtons()) { 286 min_titlebar_width += 287 minimize_button_->GetMinimumSize().width() + 288 restore_button_->GetMinimumSize().width() + 289 close_button_->GetMinimumSize().width(); 290 } 291 #endif 292 min_size.set_width(std::max(min_size.width(), min_titlebar_width)); 293 294 // Ensure that the minimum width is enough to hold a minimum width tab strip 295 // and avatar icon at their usual insets. 296 if (browser_view()->IsTabStripVisible()) { 297 TabStrip* tabstrip = browser_view()->tabstrip(); 298 const int min_tabstrip_width = tabstrip->GetMinimumSize().width(); 299 const int min_tabstrip_area_width = 300 width() - GetBoundsForTabStripAndAvatarArea(tabstrip).width() + 301 min_tabstrip_width + browser_view()->GetOTRAvatarIcon().width() + 302 kAvatarLeftSpacing + kAvatarRightSpacing; 303 min_size.set_width(std::max(min_size.width(), min_tabstrip_area_width)); 304 } 305 306 return min_size; 307 } 308 309 /////////////////////////////////////////////////////////////////////////////// 310 // OpaqueBrowserFrameView, views::NonClientFrameView implementation: 311 312 gfx::Rect OpaqueBrowserFrameView::GetBoundsForClientView() const { 313 return client_view_bounds_; 314 } 315 316 gfx::Rect OpaqueBrowserFrameView::GetWindowBoundsForClientBounds( 317 const gfx::Rect& client_bounds) const { 318 int top_height = NonClientTopBorderHeight(false); 319 int border_thickness = NonClientBorderThickness(); 320 return gfx::Rect(std::max(0, client_bounds.x() - border_thickness), 321 std::max(0, client_bounds.y() - top_height), 322 client_bounds.width() + (2 * border_thickness), 323 client_bounds.height() + top_height + border_thickness); 324 } 325 326 int OpaqueBrowserFrameView::NonClientHitTest(const gfx::Point& point) { 327 if (!bounds().Contains(point)) 328 return HTNOWHERE; 329 330 // See if the point is within the avatar menu button or within the avatar 331 // label. 332 if ((avatar_button() && 333 avatar_button()->GetMirroredBounds().Contains(point)) || 334 (avatar_label() && avatar_label()->GetMirroredBounds().Contains(point))) 335 return HTCLIENT; 336 337 int frame_component = frame()->client_view()->NonClientHitTest(point); 338 339 // See if we're in the sysmenu region. We still have to check the tabstrip 340 // first so that clicks in a tab don't get treated as sysmenu clicks. 341 gfx::Rect sysmenu_rect(IconBounds()); 342 // In maximized mode we extend the rect to the screen corner to take advantage 343 // of Fitts' Law. 344 if (frame()->IsMaximized()) 345 sysmenu_rect.SetRect(0, 0, sysmenu_rect.right(), sysmenu_rect.bottom()); 346 sysmenu_rect.set_x(GetMirroredXForRect(sysmenu_rect)); 347 if (sysmenu_rect.Contains(point)) 348 return (frame_component == HTCLIENT) ? HTCLIENT : HTSYSMENU; 349 350 if (frame_component != HTNOWHERE) 351 return frame_component; 352 353 // Then see if the point is within any of the window controls. 354 if (close_button_ && close_button_->visible() && 355 close_button_->GetMirroredBounds().Contains(point)) 356 return HTCLOSE; 357 if (restore_button_ && restore_button_->visible() && 358 restore_button_->GetMirroredBounds().Contains(point)) 359 return HTMAXBUTTON; 360 if (maximize_button_ && maximize_button_->visible() && 361 maximize_button_->GetMirroredBounds().Contains(point)) 362 return HTMAXBUTTON; 363 if (minimize_button_ && minimize_button_->visible() && 364 minimize_button_->GetMirroredBounds().Contains(point)) 365 return HTMINBUTTON; 366 367 views::WidgetDelegate* delegate = frame()->widget_delegate(); 368 if (!delegate) { 369 LOG(WARNING) << "delegate is NULL, returning safe default."; 370 return HTCAPTION; 371 } 372 int window_component = GetHTComponentForFrame(point, TopResizeHeight(), 373 NonClientBorderThickness(), kResizeAreaCornerSize, kResizeAreaCornerSize, 374 delegate->CanResize()); 375 // Fall back to the caption if no other component matches. 376 return (window_component == HTNOWHERE) ? HTCAPTION : window_component; 377 } 378 379 void OpaqueBrowserFrameView::GetWindowMask(const gfx::Size& size, 380 gfx::Path* window_mask) { 381 DCHECK(window_mask); 382 383 if (frame()->IsMaximized() || frame()->IsFullscreen()) 384 return; 385 386 views::GetDefaultWindowMask(size, window_mask); 387 } 388 389 void OpaqueBrowserFrameView::ResetWindowControls() { 390 if (!ShouldAddDefaultCaptionButtons()) 391 return; 392 restore_button_->SetState(views::CustomButton::STATE_NORMAL); 393 minimize_button_->SetState(views::CustomButton::STATE_NORMAL); 394 maximize_button_->SetState(views::CustomButton::STATE_NORMAL); 395 // The close button isn't affected by this constraint. 396 } 397 398 void OpaqueBrowserFrameView::UpdateWindowIcon() { 399 window_icon_->SchedulePaint(); 400 } 401 402 void OpaqueBrowserFrameView::UpdateWindowTitle() { 403 if (!frame()->IsFullscreen()) 404 window_title_->SchedulePaint(); 405 } 406 407 /////////////////////////////////////////////////////////////////////////////// 408 // OpaqueBrowserFrameView, views::View overrides: 409 410 void OpaqueBrowserFrameView::OnPaint(gfx::Canvas* canvas) { 411 if (frame()->IsFullscreen()) 412 return; // Nothing is visible, so don't bother to paint. 413 414 if (frame()->IsMaximized()) 415 PaintMaximizedFrameBorder(canvas); 416 else 417 PaintRestoredFrameBorder(canvas); 418 419 // The window icon and title are painted by their respective views. 420 /* TODO(pkasting): If this window is active, we should also draw a drop 421 * shadow on the title. This is tricky, because we don't want to hardcode a 422 * shadow color (since we want to work with various themes), but we can't 423 * alpha-blend either (since the Windows text APIs don't really do this). 424 * So we'd need to sample the background color at the right location and 425 * synthesize a good shadow color. */ 426 427 if (browser_view()->IsToolbarVisible()) 428 PaintToolbarBackground(canvas); 429 if (!frame()->IsMaximized()) 430 PaintRestoredClientEdge(canvas); 431 } 432 433 void OpaqueBrowserFrameView::Layout() { 434 LayoutWindowControls(); 435 LayoutTitleBar(); 436 LayoutAvatar(); 437 client_view_bounds_ = CalculateClientAreaBounds(width(), height()); 438 } 439 440 bool OpaqueBrowserFrameView::HitTestRect(const gfx::Rect& rect) const { 441 if (!views::View::HitTestRect(rect)) { 442 // |rect| is outside OpaqueBrowserFrameView's bounds. 443 return false; 444 } 445 446 // If the rect is outside the bounds of the client area, claim it. 447 // TODO(tdanderson): Implement View::ConvertRectToTarget(). 448 gfx::Point rect_in_client_view_coords_origin(rect.origin()); 449 View::ConvertPointToTarget(this, frame()->client_view(), 450 &rect_in_client_view_coords_origin); 451 gfx::Rect rect_in_client_view_coords( 452 rect_in_client_view_coords_origin, rect.size()); 453 if (!frame()->client_view()->HitTestRect(rect_in_client_view_coords)) 454 return true; 455 456 // Otherwise, claim |rect| only if it is above the bottom of the tabstrip in 457 // a non-tab portion. 458 TabStrip* tabstrip = browser_view()->tabstrip(); 459 if (!tabstrip || !browser_view()->IsTabStripVisible()) 460 return false; 461 462 gfx::Point rect_in_tabstrip_coords_origin(rect.origin()); 463 View::ConvertPointToTarget(this, tabstrip, 464 &rect_in_tabstrip_coords_origin); 465 gfx::Rect rect_in_tabstrip_coords( 466 rect_in_tabstrip_coords_origin, rect.size()); 467 468 if (rect_in_tabstrip_coords.bottom() > tabstrip->GetLocalBounds().bottom()) { 469 // |rect| is below the tabstrip. 470 return false; 471 } 472 473 if (tabstrip->HitTestRect(rect_in_tabstrip_coords)) { 474 // Claim |rect| if it is in a non-tab portion of the tabstrip. 475 // TODO(tdanderson): Pass |rect_in_tabstrip_coords| instead of its center 476 // point to TabStrip::IsPositionInWindowCaption() once 477 // GetEventHandlerForRect() is implemented. 478 return tabstrip->IsPositionInWindowCaption( 479 rect_in_tabstrip_coords.CenterPoint()); 480 } 481 482 // The window switcher button is to the right of the tabstrip but is 483 // part of the client view. 484 views::View* window_switcher_button = 485 browser_view()->window_switcher_button(); 486 if (window_switcher_button && window_switcher_button->visible()) { 487 gfx::Point rect_in_window_switcher_coords_origin(rect.origin()); 488 View::ConvertPointToTarget(this, window_switcher_button, 489 &rect_in_window_switcher_coords_origin); 490 gfx::Rect rect_in_window_switcher_coords( 491 rect_in_window_switcher_coords_origin, rect.size()); 492 493 if (window_switcher_button->HitTestRect(rect_in_window_switcher_coords)) 494 return false; 495 } 496 497 // We claim |rect| because it is above the bottom of the tabstrip, but 498 // neither in the tabstrip nor in the window switcher button. In particular, 499 // the avatar label/button is left of the tabstrip and the window controls 500 // are right of the tabstrip. 501 return true; 502 } 503 504 void OpaqueBrowserFrameView::GetAccessibleState( 505 ui::AccessibleViewState* state) { 506 state->role = ui::AccessibilityTypes::ROLE_TITLEBAR; 507 } 508 509 /////////////////////////////////////////////////////////////////////////////// 510 // OpaqueBrowserFrameView, views::ButtonListener implementation: 511 512 void OpaqueBrowserFrameView::ButtonPressed(views::Button* sender, 513 const ui::Event& event) { 514 if (sender == minimize_button_) 515 frame()->Minimize(); 516 else if (sender == maximize_button_) 517 frame()->Maximize(); 518 else if (sender == restore_button_) 519 frame()->Restore(); 520 else if (sender == close_button_) 521 frame()->Close(); 522 } 523 524 /////////////////////////////////////////////////////////////////////////////// 525 // OpaqueBrowserFrameView, TabIconView::TabContentsProvider implementation: 526 527 bool OpaqueBrowserFrameView::ShouldTabIconViewAnimate() const { 528 // This function is queried during the creation of the window as the 529 // TabIconView we host is initialized, so we need to NULL check the selected 530 // WebContents because in this condition there is not yet a selected tab. 531 WebContents* current_tab = browser_view()->GetActiveWebContents(); 532 return current_tab ? current_tab->IsLoading() : false; 533 } 534 535 gfx::ImageSkia OpaqueBrowserFrameView::GetFaviconForTabIconView() { 536 views::WidgetDelegate* delegate = frame()->widget_delegate(); 537 if (!delegate) { 538 LOG(WARNING) << "delegate is NULL, returning safe default."; 539 return gfx::ImageSkia(); 540 } 541 return delegate->GetWindowIcon(); 542 } 543 544 /////////////////////////////////////////////////////////////////////////////// 545 // OpaqueBrowserFrameView, protected: 546 547 void OpaqueBrowserFrameView::Observe( 548 int type, 549 const content::NotificationSource& source, 550 const content::NotificationDetails& details) { 551 switch (type) { 552 case chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED: 553 UpdateAvatarInfo(); 554 break; 555 default: 556 NOTREACHED() << "Got a notification we didn't register for!"; 557 break; 558 } 559 } 560 561 /////////////////////////////////////////////////////////////////////////////// 562 // OpaqueBrowserFrameView, private: 563 564 views::ImageButton* OpaqueBrowserFrameView::InitWindowCaptionButton( 565 int normal_image_id, 566 int hot_image_id, 567 int pushed_image_id, 568 int mask_image_id, 569 int accessibility_string_id) { 570 views::ImageButton* button = new views::ImageButton(this); 571 ui::ThemeProvider* tp = frame()->GetThemeProvider(); 572 button->SetImage(views::CustomButton::STATE_NORMAL, 573 tp->GetImageSkiaNamed(normal_image_id)); 574 button->SetImage(views::CustomButton::STATE_HOVERED, 575 tp->GetImageSkiaNamed(hot_image_id)); 576 button->SetImage(views::CustomButton::STATE_PRESSED, 577 tp->GetImageSkiaNamed(pushed_image_id)); 578 if (browser_view()->IsBrowserTypeNormal()) { 579 button->SetBackground( 580 tp->GetColor(ThemeProperties::COLOR_BUTTON_BACKGROUND), 581 tp->GetImageSkiaNamed(IDR_THEME_WINDOW_CONTROL_BACKGROUND), 582 tp->GetImageSkiaNamed(mask_image_id)); 583 } 584 button->SetAccessibleName( 585 l10n_util::GetStringUTF16(accessibility_string_id)); 586 AddChildView(button); 587 return button; 588 } 589 590 int OpaqueBrowserFrameView::FrameBorderThickness(bool restored) const { 591 return (!restored && (frame()->IsMaximized() || frame()->IsFullscreen())) ? 592 0 : kFrameBorderThickness; 593 } 594 595 int OpaqueBrowserFrameView::TopResizeHeight() const { 596 return FrameBorderThickness(false) - kTopResizeAdjust; 597 } 598 599 int OpaqueBrowserFrameView::NonClientBorderThickness() const { 600 // When we fill the screen, we don't show a client edge. 601 return FrameBorderThickness(false) + 602 ((frame()->IsMaximized() || frame()->IsFullscreen()) ? 603 0 : kClientEdgeThickness); 604 } 605 606 int OpaqueBrowserFrameView::CaptionButtonY(bool restored) const { 607 // Maximized buttons start at window top so that even if their images aren't 608 // drawn flush with the screen edge, they still obey Fitts' Law. 609 return (!restored && frame()->IsMaximized()) ? 610 FrameBorderThickness(false) : kFrameShadowThickness; 611 } 612 613 int OpaqueBrowserFrameView::TitlebarBottomThickness(bool restored) const { 614 return kTitlebarTopAndBottomEdgeThickness + 615 ((!restored && frame()->IsMaximized()) ? 0 : kClientEdgeThickness); 616 } 617 618 int OpaqueBrowserFrameView::IconSize() const { 619 #if defined(OS_WIN) 620 // This metric scales up if either the titlebar height or the titlebar font 621 // size are increased. 622 return GetSystemMetrics(SM_CYSMICON); 623 #else 624 return std::max(BrowserFrame::GetTitleFont().GetHeight(), kIconMinimumSize); 625 #endif 626 } 627 628 gfx::Rect OpaqueBrowserFrameView::IconBounds() const { 629 int size = IconSize(); 630 int frame_thickness = FrameBorderThickness(false); 631 int y; 632 views::WidgetDelegate* delegate = frame()->widget_delegate(); 633 if (delegate && (delegate->ShouldShowWindowIcon() || 634 delegate->ShouldShowWindowTitle())) { 635 // Our frame border has a different "3D look" than Windows'. Theirs has a 636 // more complex gradient on the top that they push their icon/title below; 637 // then the maximized window cuts this off and the icon/title are centered 638 // in the remaining space. Because the apparent shape of our border is 639 // simpler, using the same positioning makes things look slightly uncentered 640 // with restored windows, so when the window is restored, instead of 641 // calculating the remaining space from below the frame border, we calculate 642 // from below the 3D edge. 643 int unavailable_px_at_top = frame()->IsMaximized() ? 644 frame_thickness : kTitlebarTopAndBottomEdgeThickness; 645 // When the icon is shorter than the minimum space we reserve for the 646 // caption button, we vertically center it. We want to bias rounding to put 647 // extra space above the icon, since the 3D edge (+ client edge, for 648 // restored windows) below looks (to the eye) more like additional space 649 // than does the 3D edge (or nothing at all, for maximized windows) above; 650 // hence the +1. 651 y = unavailable_px_at_top + (NonClientTopBorderHeight(false) - 652 unavailable_px_at_top - size - TitlebarBottomThickness(false) + 1) / 2; 653 } else { 654 // For "browser mode" windows, we use the native positioning, which is just 655 // below the top frame border. 656 y = frame_thickness; 657 } 658 return gfx::Rect(frame_thickness + kIconLeftSpacing, y, size, size); 659 } 660 661 gfx::Rect OpaqueBrowserFrameView::GetBoundsForTabStripAndAvatarArea( 662 views::View* tabstrip) const { 663 int available_width = width(); 664 if (minimize_button_) { 665 available_width = minimize_button_->x(); 666 } else if (browser_view()->window_switcher_button()) { 667 // We don't have the sysmenu buttons in Windows 8 metro mode. However there 668 // are buttons like the window switcher which are drawn in the non client 669 // are in the BrowserView. We need to ensure that the tab strip does not 670 // draw on the window switcher button. 671 available_width -= browser_view()->window_switcher_button()->width(); 672 } 673 const int caption_spacing = frame()->IsMaximized() ? 674 kNewTabCaptionMaximizedSpacing : kNewTabCaptionRestoredSpacing; 675 const int tabstrip_x = NonClientBorderThickness(); 676 const int tabstrip_width = available_width - tabstrip_x - caption_spacing; 677 return gfx::Rect(tabstrip_x, GetTabStripInsets(false).top, 678 std::max(0, tabstrip_width), 679 tabstrip->GetPreferredSize().height()); 680 } 681 682 void OpaqueBrowserFrameView::PaintRestoredFrameBorder(gfx::Canvas* canvas) { 683 frame_background_->set_frame_color(GetFrameColor()); 684 frame_background_->set_theme_image(GetFrameImage()); 685 frame_background_->set_theme_overlay_image(GetFrameOverlayImage()); 686 frame_background_->set_top_area_height(GetTopAreaHeight()); 687 688 ui::ThemeProvider* tp = GetThemeProvider(); 689 frame_background_->SetSideImages( 690 tp->GetImageSkiaNamed(IDR_WINDOW_LEFT_SIDE), 691 tp->GetImageSkiaNamed(IDR_WINDOW_TOP_CENTER), 692 tp->GetImageSkiaNamed(IDR_WINDOW_RIGHT_SIDE), 693 tp->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_CENTER)); 694 frame_background_->SetCornerImages( 695 tp->GetImageSkiaNamed(IDR_WINDOW_TOP_LEFT_CORNER), 696 tp->GetImageSkiaNamed(IDR_WINDOW_TOP_RIGHT_CORNER), 697 tp->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_LEFT_CORNER), 698 tp->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_RIGHT_CORNER)); 699 frame_background_->PaintRestored(canvas, this); 700 701 // Note: When we don't have a toolbar, we need to draw some kind of bottom 702 // edge here. Because the App Window graphics we use for this have an 703 // attached client edge and their sizing algorithm is a little involved, we do 704 // all this in PaintRestoredClientEdge(). 705 } 706 707 void OpaqueBrowserFrameView::PaintMaximizedFrameBorder(gfx::Canvas* canvas) { 708 frame_background_->set_frame_color(GetFrameColor()); 709 frame_background_->set_theme_image(GetFrameImage()); 710 frame_background_->set_theme_overlay_image(GetFrameOverlayImage()); 711 frame_background_->set_top_area_height(GetTopAreaHeight()); 712 713 // Theme frame must be aligned with the tabstrip as if we were 714 // in restored mode. Note that the top of the tabstrip is 715 // kTabstripTopShadowThickness px off the top of the screen. 716 int theme_background_y = -(GetTabStripInsets(true).top + 717 kTabstripTopShadowThickness); 718 frame_background_->set_theme_background_y(theme_background_y); 719 720 frame_background_->PaintMaximized(canvas, this); 721 722 // TODO(jamescook): Migrate this into FrameBackground. 723 if (!browser_view()->IsToolbarVisible()) { 724 // There's no toolbar to edge the frame border, so we need to draw a bottom 725 // edge. The graphic we use for this has a built in client edge, so we clip 726 // it off the bottom. 727 gfx::ImageSkia* top_center = 728 GetThemeProvider()->GetImageSkiaNamed(IDR_APP_TOP_CENTER); 729 int edge_height = top_center->height() - kClientEdgeThickness; 730 canvas->TileImageInt(*top_center, 0, 731 frame()->client_view()->y() - edge_height, width(), edge_height); 732 } 733 } 734 735 void OpaqueBrowserFrameView::PaintToolbarBackground(gfx::Canvas* canvas) { 736 gfx::Rect toolbar_bounds(browser_view()->GetToolbarBounds()); 737 if (toolbar_bounds.IsEmpty()) 738 return; 739 gfx::Point toolbar_origin(toolbar_bounds.origin()); 740 ConvertPointToTarget(browser_view(), this, &toolbar_origin); 741 toolbar_bounds.set_origin(toolbar_origin); 742 743 int x = toolbar_bounds.x(); 744 int w = toolbar_bounds.width(); 745 int y = toolbar_bounds.y(); 746 int h = toolbar_bounds.height(); 747 748 // Gross hack: We split the toolbar images into two pieces, since sometimes 749 // (popup mode) the toolbar isn't tall enough to show the whole image. The 750 // split happens between the top shadow section and the bottom gradient 751 // section so that we never break the gradient. 752 int split_point = kFrameShadowThickness * 2; 753 int bottom_y = y + split_point; 754 ui::ThemeProvider* tp = GetThemeProvider(); 755 gfx::ImageSkia* toolbar_left = tp->GetImageSkiaNamed( 756 IDR_CONTENT_TOP_LEFT_CORNER); 757 int bottom_edge_height = std::min(toolbar_left->height(), h) - split_point; 758 759 // Split our canvas out so we can mask out the corners of the toolbar 760 // without masking out the frame. 761 canvas->SaveLayerAlpha( 762 255, gfx::Rect(x - kClientEdgeThickness, y, w + kClientEdgeThickness * 3, 763 h)); 764 765 // Paint the bottom rect. 766 canvas->FillRect(gfx::Rect(x, bottom_y, w, bottom_edge_height), 767 tp->GetColor(ThemeProperties::COLOR_TOOLBAR)); 768 769 // Tile the toolbar image starting at the frame edge on the left and where the 770 // horizontal tabstrip is (or would be) on the top. 771 gfx::ImageSkia* theme_toolbar = tp->GetImageSkiaNamed(IDR_THEME_TOOLBAR); 772 canvas->TileImageInt(*theme_toolbar, 773 x + GetThemeBackgroundXInset(), 774 bottom_y - GetTabStripInsets(false).top, 775 x, bottom_y, w, theme_toolbar->height()); 776 777 // Draw rounded corners for the tab. 778 gfx::ImageSkia* toolbar_left_mask = 779 tp->GetImageSkiaNamed(IDR_CONTENT_TOP_LEFT_CORNER_MASK); 780 gfx::ImageSkia* toolbar_right_mask = 781 tp->GetImageSkiaNamed(IDR_CONTENT_TOP_RIGHT_CORNER_MASK); 782 783 // We mask out the corners by using the DestinationIn transfer mode, 784 // which keeps the RGB pixels from the destination and the alpha from 785 // the source. 786 SkPaint paint; 787 paint.setXfermodeMode(SkXfermode::kDstIn_Mode); 788 789 // Mask the left edge. 790 int left_x = x - kContentEdgeShadowThickness; 791 canvas->DrawImageInt(*toolbar_left_mask, 0, 0, toolbar_left_mask->width(), 792 split_point, left_x, y, toolbar_left_mask->width(), 793 split_point, false, paint); 794 canvas->DrawImageInt(*toolbar_left_mask, 0, 795 toolbar_left_mask->height() - bottom_edge_height, 796 toolbar_left_mask->width(), bottom_edge_height, left_x, bottom_y, 797 toolbar_left_mask->width(), bottom_edge_height, false, paint); 798 799 // Mask the right edge. 800 int right_x = 801 x + w - toolbar_right_mask->width() + kContentEdgeShadowThickness; 802 canvas->DrawImageInt(*toolbar_right_mask, 0, 0, toolbar_right_mask->width(), 803 split_point, right_x, y, toolbar_right_mask->width(), 804 split_point, false, paint); 805 canvas->DrawImageInt(*toolbar_right_mask, 0, 806 toolbar_right_mask->height() - bottom_edge_height, 807 toolbar_right_mask->width(), bottom_edge_height, right_x, bottom_y, 808 toolbar_right_mask->width(), bottom_edge_height, false, paint); 809 canvas->Restore(); 810 811 canvas->DrawImageInt(*toolbar_left, 0, 0, toolbar_left->width(), split_point, 812 left_x, y, toolbar_left->width(), split_point, false); 813 canvas->DrawImageInt(*toolbar_left, 0, 814 toolbar_left->height() - bottom_edge_height, toolbar_left->width(), 815 bottom_edge_height, left_x, bottom_y, toolbar_left->width(), 816 bottom_edge_height, false); 817 818 gfx::ImageSkia* toolbar_center = 819 tp->GetImageSkiaNamed(IDR_CONTENT_TOP_CENTER); 820 canvas->TileImageInt(*toolbar_center, 0, 0, left_x + toolbar_left->width(), 821 y, right_x - (left_x + toolbar_left->width()), 822 split_point); 823 824 gfx::ImageSkia* toolbar_right = tp->GetImageSkiaNamed( 825 IDR_CONTENT_TOP_RIGHT_CORNER); 826 canvas->DrawImageInt(*toolbar_right, 0, 0, toolbar_right->width(), 827 split_point, right_x, y, toolbar_right->width(), split_point, false); 828 canvas->DrawImageInt(*toolbar_right, 0, 829 toolbar_right->height() - bottom_edge_height, toolbar_right->width(), 830 bottom_edge_height, right_x, bottom_y, toolbar_right->width(), 831 bottom_edge_height, false); 832 833 // Draw the content/toolbar separator. 834 canvas->FillRect( 835 gfx::Rect(x + kClientEdgeThickness, 836 toolbar_bounds.bottom() - kClientEdgeThickness, 837 w - (2 * kClientEdgeThickness), 838 kClientEdgeThickness), 839 ThemeProperties::GetDefaultColor( 840 ThemeProperties::COLOR_TOOLBAR_SEPARATOR)); 841 } 842 843 void OpaqueBrowserFrameView::PaintRestoredClientEdge(gfx::Canvas* canvas) { 844 ui::ThemeProvider* tp = GetThemeProvider(); 845 int client_area_top = frame()->client_view()->y(); 846 int image_top = client_area_top; 847 848 gfx::Rect client_area_bounds = CalculateClientAreaBounds(width(), height()); 849 SkColor toolbar_color = tp->GetColor(ThemeProperties::COLOR_TOOLBAR); 850 851 if (browser_view()->IsToolbarVisible()) { 852 // The client edge images always start below the toolbar corner images. The 853 // client edge filled rects start there or at the bottom of the toolbar, 854 // whichever is shorter. 855 gfx::Rect toolbar_bounds(browser_view()->GetToolbarBounds()); 856 image_top += toolbar_bounds.y() + 857 tp->GetImageSkiaNamed(IDR_CONTENT_TOP_LEFT_CORNER)->height(); 858 client_area_top = std::min(image_top, 859 client_area_top + toolbar_bounds.bottom() - kClientEdgeThickness); 860 } else if (!browser_view()->IsTabStripVisible()) { 861 // The toolbar isn't going to draw a client edge for us, so draw one 862 // ourselves. 863 gfx::ImageSkia* top_left = tp->GetImageSkiaNamed(IDR_APP_TOP_LEFT); 864 gfx::ImageSkia* top_center = tp->GetImageSkiaNamed(IDR_APP_TOP_CENTER); 865 gfx::ImageSkia* top_right = tp->GetImageSkiaNamed(IDR_APP_TOP_RIGHT); 866 int top_edge_y = client_area_top - top_center->height(); 867 int height = client_area_top - top_edge_y; 868 869 canvas->DrawImageInt(*top_left, 0, 0, top_left->width(), height, 870 client_area_bounds.x() - top_left->width(), top_edge_y, 871 top_left->width(), height, false); 872 canvas->TileImageInt(*top_center, 0, 0, client_area_bounds.x(), top_edge_y, 873 client_area_bounds.width(), std::min(height, top_center->height())); 874 canvas->DrawImageInt(*top_right, 0, 0, top_right->width(), height, 875 client_area_bounds.right(), top_edge_y, 876 top_right->width(), height, false); 877 878 // Draw the toolbar color across the top edge. 879 canvas->FillRect(gfx::Rect(client_area_bounds.x() - kClientEdgeThickness, 880 client_area_top - kClientEdgeThickness, 881 client_area_bounds.width() + (2 * kClientEdgeThickness), 882 kClientEdgeThickness), toolbar_color); 883 } 884 885 int client_area_bottom = 886 std::max(client_area_top, height() - NonClientBorderThickness()); 887 int image_height = client_area_bottom - image_top; 888 889 // Draw the client edge images. 890 gfx::ImageSkia* right = tp->GetImageSkiaNamed(IDR_CONTENT_RIGHT_SIDE); 891 canvas->TileImageInt(*right, client_area_bounds.right(), image_top, 892 right->width(), image_height); 893 canvas->DrawImageInt( 894 *tp->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_RIGHT_CORNER), 895 client_area_bounds.right(), client_area_bottom); 896 gfx::ImageSkia* bottom = tp->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_CENTER); 897 canvas->TileImageInt(*bottom, client_area_bounds.x(), 898 client_area_bottom, client_area_bounds.width(), 899 bottom->height()); 900 gfx::ImageSkia* bottom_left = 901 tp->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_LEFT_CORNER); 902 canvas->DrawImageInt(*bottom_left, 903 client_area_bounds.x() - bottom_left->width(), client_area_bottom); 904 gfx::ImageSkia* left = tp->GetImageSkiaNamed(IDR_CONTENT_LEFT_SIDE); 905 canvas->TileImageInt(*left, client_area_bounds.x() - left->width(), 906 image_top, left->width(), image_height); 907 908 // Draw the toolbar color so that the client edges show the right color even 909 // where not covered by the toolbar image. NOTE: We do this after drawing the 910 // images because the images are meant to alpha-blend atop the frame whereas 911 // these rects are meant to be fully opaque, without anything overlaid. 912 canvas->FillRect(gfx::Rect(client_area_bounds.x() - kClientEdgeThickness, 913 client_area_top, kClientEdgeThickness, 914 client_area_bottom + kClientEdgeThickness - client_area_top), 915 toolbar_color); 916 canvas->FillRect(gfx::Rect(client_area_bounds.x(), client_area_bottom, 917 client_area_bounds.width(), kClientEdgeThickness), 918 toolbar_color); 919 canvas->FillRect(gfx::Rect(client_area_bounds.right(), client_area_top, 920 kClientEdgeThickness, 921 client_area_bottom + kClientEdgeThickness - client_area_top), 922 toolbar_color); 923 } 924 925 SkColor OpaqueBrowserFrameView::GetFrameColor() const { 926 bool is_incognito = browser_view()->IsOffTheRecord(); 927 if (browser_view()->IsBrowserTypeNormal()) { 928 if (ShouldPaintAsActive()) { 929 return GetThemeProvider()->GetColor(is_incognito ? 930 ThemeProperties::COLOR_FRAME_INCOGNITO : 931 ThemeProperties::COLOR_FRAME); 932 } 933 return GetThemeProvider()->GetColor(is_incognito ? 934 ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE : 935 ThemeProperties::COLOR_FRAME_INACTIVE); 936 } 937 // Never theme app and popup windows. 938 if (ShouldPaintAsActive()) { 939 return ThemeProperties::GetDefaultColor(is_incognito ? 940 ThemeProperties::COLOR_FRAME_INCOGNITO : ThemeProperties::COLOR_FRAME); 941 } 942 return ThemeProperties::GetDefaultColor(is_incognito ? 943 ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE : 944 ThemeProperties::COLOR_FRAME_INACTIVE); 945 } 946 947 gfx::ImageSkia* OpaqueBrowserFrameView::GetFrameImage() const { 948 bool is_incognito = browser_view()->IsOffTheRecord(); 949 int resource_id; 950 if (browser_view()->IsBrowserTypeNormal()) { 951 if (ShouldPaintAsActive()) { 952 resource_id = is_incognito ? 953 IDR_THEME_FRAME_INCOGNITO : IDR_THEME_FRAME; 954 } else { 955 resource_id = is_incognito ? 956 IDR_THEME_FRAME_INCOGNITO_INACTIVE : IDR_THEME_FRAME_INACTIVE; 957 } 958 return GetThemeProvider()->GetImageSkiaNamed(resource_id); 959 } 960 // Never theme app and popup windows. 961 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 962 if (ShouldPaintAsActive()) { 963 resource_id = is_incognito ? 964 IDR_THEME_FRAME_INCOGNITO : IDR_FRAME; 965 } else { 966 resource_id = is_incognito ? 967 IDR_THEME_FRAME_INCOGNITO_INACTIVE : IDR_THEME_FRAME_INACTIVE; 968 } 969 return rb.GetImageSkiaNamed(resource_id); 970 } 971 972 gfx::ImageSkia* OpaqueBrowserFrameView::GetFrameOverlayImage() const { 973 ui::ThemeProvider* tp = GetThemeProvider(); 974 if (tp->HasCustomImage(IDR_THEME_FRAME_OVERLAY) && 975 browser_view()->IsBrowserTypeNormal() && 976 !browser_view()->IsOffTheRecord()) { 977 return tp->GetImageSkiaNamed(ShouldPaintAsActive() ? 978 IDR_THEME_FRAME_OVERLAY : IDR_THEME_FRAME_OVERLAY_INACTIVE); 979 } 980 return NULL; 981 } 982 983 int OpaqueBrowserFrameView::GetTopAreaHeight() const { 984 gfx::ImageSkia* frame_image = GetFrameImage(); 985 int top_area_height = frame_image->height(); 986 if (browser_view()->IsTabStripVisible()) { 987 top_area_height = std::max(top_area_height, 988 GetBoundsForTabStrip(browser_view()->tabstrip()).bottom()); 989 } 990 return top_area_height; 991 } 992 993 void OpaqueBrowserFrameView::LayoutWindowControls() { 994 if (!ShouldAddDefaultCaptionButtons()) 995 return; 996 bool is_maximized = frame()->IsMaximized(); 997 close_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT, 998 views::ImageButton::ALIGN_BOTTOM); 999 int caption_y = CaptionButtonY(false); 1000 // There should always be the same number of non-shadow pixels visible to the 1001 // side of the caption buttons. In maximized mode we extend the rightmost 1002 // button to the screen corner to obey Fitts' Law. 1003 int right_extra_width = is_maximized ? 1004 (kFrameBorderThickness - kFrameShadowThickness) : 0; 1005 gfx::Size close_button_size = close_button_->GetPreferredSize(); 1006 close_button_->SetBounds(width() - FrameBorderThickness(false) - 1007 right_extra_width - close_button_size.width(), caption_y, 1008 close_button_size.width() + right_extra_width, 1009 close_button_size.height()); 1010 1011 // When the window is restored, we show a maximized button; otherwise, we show 1012 // a restore button. 1013 bool is_restored = !is_maximized && !frame()->IsMinimized(); 1014 views::ImageButton* invisible_button = is_restored ? 1015 restore_button_ : maximize_button_; 1016 invisible_button->SetVisible(false); 1017 1018 views::ImageButton* visible_button = is_restored ? 1019 maximize_button_ : restore_button_; 1020 visible_button->SetVisible(true); 1021 visible_button->SetImageAlignment(views::ImageButton::ALIGN_LEFT, 1022 views::ImageButton::ALIGN_BOTTOM); 1023 gfx::Size visible_button_size = visible_button->GetPreferredSize(); 1024 visible_button->SetBounds(close_button_->x() - visible_button_size.width(), 1025 caption_y, visible_button_size.width(), 1026 visible_button_size.height()); 1027 1028 minimize_button_->SetVisible(true); 1029 minimize_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT, 1030 views::ImageButton::ALIGN_BOTTOM); 1031 gfx::Size minimize_button_size = minimize_button_->GetPreferredSize(); 1032 minimize_button_->SetBounds( 1033 visible_button->x() - minimize_button_size.width(), caption_y, 1034 minimize_button_size.width(), 1035 minimize_button_size.height()); 1036 } 1037 1038 void OpaqueBrowserFrameView::LayoutTitleBar() { 1039 const views::WidgetDelegate* delegate = frame()->widget_delegate(); 1040 if (delegate) { 1041 gfx::Rect icon_bounds(IconBounds()); 1042 if (delegate->ShouldShowWindowIcon()) 1043 window_icon_->SetBoundsRect(icon_bounds); 1044 1045 window_title_->SetVisible(delegate->ShouldShowWindowTitle()); 1046 if (delegate->ShouldShowWindowTitle()) { 1047 window_title_->SetText(delegate->GetWindowTitle()); 1048 const int title_x = delegate->ShouldShowWindowIcon() ? 1049 icon_bounds.right() + kIconTitleSpacing : icon_bounds.x(); 1050 window_title_->SetBounds(title_x, icon_bounds.y(), 1051 std::max(0, minimize_button_->x() - kTitleLogoSpacing - title_x), 1052 icon_bounds.height()); 1053 } 1054 } 1055 } 1056 1057 void OpaqueBrowserFrameView::LayoutAvatar() { 1058 // Even though the avatar is used for both incognito and profiles we always 1059 // use the incognito icon to layout the avatar button. The profile icon 1060 // can be customized so we can't depend on its size to perform layout. 1061 gfx::ImageSkia incognito_icon = browser_view()->GetOTRAvatarIcon(); 1062 1063 int avatar_bottom = GetTabStripInsets(false).top + 1064 browser_view()->GetTabStripHeight() - kAvatarBottomSpacing; 1065 int avatar_restored_y = avatar_bottom - incognito_icon.height(); 1066 int avatar_y = frame()->IsMaximized() ? 1067 (NonClientTopBorderHeight(false) + kTabstripTopShadowThickness) : 1068 avatar_restored_y; 1069 avatar_bounds_.SetRect(NonClientBorderThickness() + kAvatarLeftSpacing, 1070 avatar_y, incognito_icon.width(), 1071 browser_view()->ShouldShowAvatar() ? (avatar_bottom - avatar_y) : 0); 1072 if (avatar_button()) 1073 avatar_button()->SetBoundsRect(avatar_bounds_); 1074 1075 if (avatar_label()) { 1076 // Space between the bottom of the avatar and the bottom of the avatar 1077 // label. 1078 const int kAvatarLabelBottomSpacing = 3; 1079 // Space between the frame border and the left edge of the avatar label. 1080 const int kAvatarLabelLeftSpacing = -1; 1081 gfx::Size label_size = avatar_label()->GetPreferredSize(); 1082 gfx::Rect label_bounds( 1083 FrameBorderThickness(false) + kAvatarLabelLeftSpacing, 1084 avatar_bottom - kAvatarLabelBottomSpacing - label_size.height(), 1085 label_size.width(), 1086 browser_view()->ShouldShowAvatar() ? label_size.height() : 0); 1087 avatar_label()->SetBoundsRect(label_bounds); 1088 } 1089 } 1090 1091 gfx::Rect OpaqueBrowserFrameView::CalculateClientAreaBounds(int width, 1092 int height) const { 1093 int top_height = NonClientTopBorderHeight(false); 1094 int border_thickness = NonClientBorderThickness(); 1095 return gfx::Rect(border_thickness, top_height, 1096 std::max(0, width - (2 * border_thickness)), 1097 std::max(0, height - GetReservedHeight() - 1098 top_height - border_thickness)); 1099 } 1100