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