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