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