1 // Copyright 2013 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_layout.h" 6 7 #include "base/command_line.h" 8 #include "chrome/browser/profiles/profiles_state.h" 9 #include "chrome/browser/ui/views/profiles/avatar_label.h" 10 #include "chrome/browser/ui/views/profiles/avatar_menu_button.h" 11 #include "chrome/browser/ui/views/tabs/tab_strip.h" 12 #include "chrome/common/chrome_switches.h" 13 #include "components/signin/core/common/profile_management_switches.h" 14 #include "ui/gfx/font.h" 15 #include "ui/views/controls/button/image_button.h" 16 #include "ui/views/controls/label.h" 17 18 namespace { 19 20 // Besides the frame border, there's another 9 px of empty space atop the 21 // window in restored mode, to use to drag the window around. 22 const int kNonClientRestoredExtraThickness = 9; 23 24 // The titlebar never shrinks too short to show the caption button plus some 25 // padding below it. 26 const int kCaptionButtonHeightWithPadding = 19; 27 28 // There is a 5 px gap between the title text and the caption buttons. 29 const int kTitleLogoSpacing = 5; 30 31 // The frame border is only visible in restored mode and is hardcoded to 4 px on 32 // each side regardless of the system window border size. 33 const int kFrameBorderThickness = 4; 34 35 // The titlebar has a 2 px 3D edge along the top and bottom. 36 const int kTitlebarTopAndBottomEdgeThickness = 2; 37 38 // The icon is inset 2 px from the left frame border. 39 const int kIconLeftSpacing = 2; 40 41 // There is a 4 px gap between the icon and the title text. 42 const int kIconTitleSpacing = 4; 43 44 // The avatar ends 2 px above the bottom of the tabstrip (which, given the 45 // way the tabstrip draws its bottom edge, will appear like a 1 px gap to the 46 // user). 47 const int kAvatarBottomSpacing = 2; 48 49 // Space between the frame border and the edge of the avatar. 50 const int kAvatarOuterSpacing = 2; 51 52 // Space between the edge of the avatar and the tabstrip. 53 const int kAvatarInnerSpacing = 4; 54 55 // Space between the trailing edge of the avatar label and the tabstrip. 56 const int kAvatarLabelInnerSpacing = 10; 57 58 // How far the new avatar button is from the closest caption button. 59 const int kNewAvatarButtonOffset = 5; 60 61 // When the title bar is in its normal two row mode (usually the case for 62 // restored windows), the New Tab button isn't at the same height as the caption 63 // buttons, but the space will look cluttered if it actually slides under them, 64 // so we stop it when the gap between the two is down to 5 px. 65 const int kNewTabCaptionNormalSpacing = 5; 66 67 // When the title bar is condensed to one row (as when maximized), the New Tab 68 // button and the caption buttons are at similar vertical coordinates, so we 69 // need to reserve a larger, 16 px gap to avoid looking too cluttered. 70 const int kNewTabCaptionCondensedSpacing = 16; 71 72 // If there are no caption buttons to the right of the New Tab button, we 73 // reserve a small 5px gap, regardless of whether the window is maximized. This 74 // overrides the two previous constants. 75 const int kNewTabNoCaptionButtonsSpacing = 5; 76 77 // The top 3 px of the tabstrip is shadow; in maximized mode we push this off 78 // the top of the screen so the tabs appear flush against the screen edge. 79 const int kTabstripTopShadowThickness = 3; 80 81 // How far to indent the tabstrip from the left side of the screen when there 82 // is no avatar icon. 83 const int kTabStripIndent = -6; 84 85 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) 86 // Default extra space between the top of the frame and the top of the window 87 // caption buttons. 88 const int kExtraCaption = 2; 89 90 // Default extra spacing between individual window caption buttons. 91 const int kCaptionButtonSpacing = 2; 92 #else 93 const int kExtraCaption = 0; 94 const int kCaptionButtonSpacing = 0; 95 #endif 96 97 } // namespace 98 99 /////////////////////////////////////////////////////////////////////////////// 100 // OpaqueBrowserFrameView, public: 101 102 OpaqueBrowserFrameViewLayout::OpaqueBrowserFrameViewLayout( 103 OpaqueBrowserFrameViewLayoutDelegate* delegate) 104 : delegate_(delegate), 105 leading_button_start_(0), 106 trailing_button_start_(0), 107 minimum_size_for_buttons_(0), 108 has_leading_buttons_(false), 109 has_trailing_buttons_(false), 110 extra_caption_y_(kExtraCaption), 111 window_caption_spacing_(kCaptionButtonSpacing), 112 minimize_button_(NULL), 113 maximize_button_(NULL), 114 restore_button_(NULL), 115 close_button_(NULL), 116 window_icon_(NULL), 117 window_title_(NULL), 118 avatar_label_(NULL), 119 avatar_button_(NULL), 120 new_avatar_button_(NULL) { 121 trailing_buttons_.push_back(views::FRAME_BUTTON_MINIMIZE); 122 trailing_buttons_.push_back(views::FRAME_BUTTON_MAXIMIZE); 123 trailing_buttons_.push_back(views::FRAME_BUTTON_CLOSE); 124 } 125 126 OpaqueBrowserFrameViewLayout::~OpaqueBrowserFrameViewLayout() {} 127 128 void OpaqueBrowserFrameViewLayout::SetButtonOrdering( 129 const std::vector<views::FrameButton>& leading_buttons, 130 const std::vector<views::FrameButton>& trailing_buttons) { 131 leading_buttons_ = leading_buttons; 132 trailing_buttons_ = trailing_buttons; 133 } 134 135 gfx::Rect OpaqueBrowserFrameViewLayout::GetBoundsForTabStrip( 136 const gfx::Size& tabstrip_preferred_size, 137 int available_width) const { 138 available_width -= trailing_button_start_; 139 available_width -= leading_button_start_; 140 141 const int caption_spacing = NewTabCaptionSpacing(); 142 const int tabstrip_width = available_width - caption_spacing; 143 gfx::Rect bounds(leading_button_start_, GetTabStripInsetsTop(false), 144 std::max(0, tabstrip_width), 145 tabstrip_preferred_size.height()); 146 147 int leading_tabstrip_indent = kTabStripIndent; 148 if (delegate_->ShouldShowAvatar() && !ShouldAvatarBeOnRight()) { 149 if (avatar_label_ && avatar_label_->bounds().width()) 150 leading_tabstrip_indent += kAvatarLabelInnerSpacing; 151 else 152 leading_tabstrip_indent += kAvatarInnerSpacing; 153 } 154 bounds.Inset(leading_tabstrip_indent, 0, 0, 0); 155 return bounds; 156 } 157 158 gfx::Size OpaqueBrowserFrameViewLayout::GetMinimumSize( 159 int available_width) const { 160 gfx::Size min_size = delegate_->GetBrowserViewMinimumSize(); 161 int border_thickness = NonClientBorderThickness(); 162 min_size.Enlarge(2 * border_thickness, 163 NonClientTopBorderHeight(false) + border_thickness); 164 165 // Ensure that we can, at minimum, hold our window controls and avatar icon. 166 min_size.set_width(std::max(min_size.width(), minimum_size_for_buttons_)); 167 168 // Ensure that the minimum width is enough to hold a minimum width tab strip 169 // at its usual insets. 170 if (delegate_->IsTabStripVisible()) { 171 gfx::Size preferred_size = delegate_->GetTabstripPreferredSize(); 172 const int min_tabstrip_width = preferred_size.width(); 173 const int caption_spacing = NewTabCaptionSpacing(); 174 min_size.Enlarge(min_tabstrip_width + caption_spacing, 0); 175 } 176 177 return min_size; 178 } 179 180 gfx::Rect OpaqueBrowserFrameViewLayout::GetWindowBoundsForClientBounds( 181 const gfx::Rect& client_bounds) const { 182 int top_height = NonClientTopBorderHeight(false); 183 int border_thickness = NonClientBorderThickness(); 184 return gfx::Rect(std::max(0, client_bounds.x() - border_thickness), 185 std::max(0, client_bounds.y() - top_height), 186 client_bounds.width() + (2 * border_thickness), 187 client_bounds.height() + top_height + border_thickness); 188 } 189 190 int OpaqueBrowserFrameViewLayout::FrameBorderThickness(bool restored) const { 191 return (!restored && (IsTitleBarCondensed() || 192 delegate_->IsFullscreen())) ? 193 0 : kFrameBorderThickness; 194 } 195 196 int OpaqueBrowserFrameViewLayout::NonClientBorderThickness() const { 197 // When we fill the screen, we don't show a client edge. 198 return FrameBorderThickness(false) + 199 ((IsTitleBarCondensed() || delegate_->IsFullscreen()) ? 200 0 : views::NonClientFrameView::kClientEdgeThickness); 201 } 202 203 int OpaqueBrowserFrameViewLayout::NonClientTopBorderHeight( 204 bool restored) const { 205 if (delegate_->ShouldShowWindowTitle()) { 206 return std::max(FrameBorderThickness(restored) + delegate_->GetIconSize(), 207 CaptionButtonY(restored) + kCaptionButtonHeightWithPadding) + 208 TitlebarBottomThickness(restored); 209 } 210 211 int thickness = FrameBorderThickness(restored); 212 if (!restored && delegate_->IsTabStripVisible() && 213 (!delegate_->ShouldLeaveOffsetNearTopBorder() || IsTitleBarCondensed())) { 214 thickness -= kTabstripTopShadowThickness; 215 } 216 return thickness; 217 } 218 219 int OpaqueBrowserFrameViewLayout::GetTabStripInsetsTop(bool restored) const { 220 return NonClientTopBorderHeight(restored) + ((!restored && 221 (!delegate_->ShouldLeaveOffsetNearTopBorder() || 222 IsTitleBarCondensed() || 223 delegate_->IsFullscreen())) ? 224 0 : kNonClientRestoredExtraThickness); 225 } 226 227 int OpaqueBrowserFrameViewLayout::TitlebarBottomThickness(bool restored) const { 228 return kTitlebarTopAndBottomEdgeThickness + 229 ((!restored && IsTitleBarCondensed()) ? 0 : 230 views::NonClientFrameView::kClientEdgeThickness); 231 } 232 233 int OpaqueBrowserFrameViewLayout::CaptionButtonY(bool restored) const { 234 // Maximized buttons start at window top, since the window has no border. This 235 // offset is for the image (the actual clickable bounds extend all the way to 236 // the top to take Fitts' Law into account). 237 return ((!restored && IsTitleBarCondensed()) ? 238 FrameBorderThickness(false) : 239 views::NonClientFrameView::kFrameShadowThickness) + extra_caption_y_; 240 } 241 242 gfx::Rect OpaqueBrowserFrameViewLayout::IconBounds() const { 243 return window_icon_bounds_; 244 } 245 246 gfx::Rect OpaqueBrowserFrameViewLayout::CalculateClientAreaBounds( 247 int width, 248 int height) const { 249 int top_height = NonClientTopBorderHeight(false); 250 int border_thickness = NonClientBorderThickness(); 251 return gfx::Rect(border_thickness, top_height, 252 std::max(0, width - (2 * border_thickness)), 253 std::max(0, height - top_height - border_thickness)); 254 } 255 256 bool OpaqueBrowserFrameViewLayout::IsTitleBarCondensed() const { 257 // If there are no caption buttons, there is no need to have an uncondensed 258 // title bar. If the window is maximized, the title bar is condensed 259 // regardless of whether there are caption buttons. 260 return !delegate_->ShouldShowCaptionButtons() || delegate_->IsMaximized(); 261 } 262 263 /////////////////////////////////////////////////////////////////////////////// 264 // OpaqueBrowserFrameView, private: 265 266 bool OpaqueBrowserFrameViewLayout::ShouldAvatarBeOnRight() const { 267 // The avatar should be shown either on the end of the left or the beginning 268 // of the right depending on which side has fewer buttons. 269 return trailing_buttons_.size() < leading_buttons_.size(); 270 } 271 272 int OpaqueBrowserFrameViewLayout::NewTabCaptionSpacing() const { 273 return has_trailing_buttons_ 274 ? (IsTitleBarCondensed() ? kNewTabCaptionCondensedSpacing 275 : kNewTabCaptionNormalSpacing) 276 : kNewTabNoCaptionButtonsSpacing; 277 } 278 279 void OpaqueBrowserFrameViewLayout::LayoutWindowControls(views::View* host) { 280 int caption_y = CaptionButtonY(false); 281 282 // Keep a list of all buttons that we don't show. 283 std::vector<views::FrameButton> buttons_not_shown; 284 buttons_not_shown.push_back(views::FRAME_BUTTON_MAXIMIZE); 285 buttons_not_shown.push_back(views::FRAME_BUTTON_MINIMIZE); 286 buttons_not_shown.push_back(views::FRAME_BUTTON_CLOSE); 287 288 if (delegate_->ShouldShowCaptionButtons()) { 289 for (std::vector<views::FrameButton>::const_iterator it = 290 leading_buttons_.begin(); it != leading_buttons_.end(); ++it) { 291 ConfigureButton(host, *it, ALIGN_LEADING, caption_y); 292 buttons_not_shown.erase( 293 std::remove(buttons_not_shown.begin(), buttons_not_shown.end(), *it), 294 buttons_not_shown.end()); 295 } 296 297 for (std::vector<views::FrameButton>::const_reverse_iterator it = 298 trailing_buttons_.rbegin(); it != trailing_buttons_.rend(); ++it) { 299 ConfigureButton(host, *it, ALIGN_TRAILING, caption_y); 300 buttons_not_shown.erase( 301 std::remove(buttons_not_shown.begin(), buttons_not_shown.end(), *it), 302 buttons_not_shown.end()); 303 } 304 } 305 306 for (std::vector<views::FrameButton>::const_iterator it = 307 buttons_not_shown.begin(); it != buttons_not_shown.end(); ++it) { 308 HideButton(*it); 309 } 310 } 311 312 void OpaqueBrowserFrameViewLayout::LayoutTitleBar(views::View* host) { 313 bool use_hidden_icon_location = true; 314 315 int size = delegate_->GetIconSize(); 316 int frame_thickness = FrameBorderThickness(false); 317 bool should_show_icon = delegate_->ShouldShowWindowIcon() && window_icon_; 318 bool should_show_title = delegate_->ShouldShowWindowTitle() && window_title_; 319 320 if (should_show_icon || should_show_title) { 321 use_hidden_icon_location = false; 322 323 // Our frame border has a different "3D look" than Windows'. Theirs has 324 // a more complex gradient on the top that they push their icon/title 325 // below; then the maximized window cuts this off and the icon/title are 326 // centered in the remaining space. Because the apparent shape of our 327 // border is simpler, using the same positioning makes things look 328 // slightly uncentered with restored windows, so when the window is 329 // restored, instead of calculating the remaining space from below the 330 // frame border, we calculate from below the 3D edge. 331 int unavailable_px_at_top = IsTitleBarCondensed() ? 332 frame_thickness : kTitlebarTopAndBottomEdgeThickness; 333 // When the icon is shorter than the minimum space we reserve for the 334 // caption button, we vertically center it. We want to bias rounding to 335 // put extra space above the icon, since the 3D edge (+ client edge, for 336 // restored windows) below looks (to the eye) more like additional space 337 // than does the 3D edge (or nothing at all, for maximized windows) 338 // above; hence the +1. 339 int y = unavailable_px_at_top + (NonClientTopBorderHeight(false) - 340 unavailable_px_at_top - size - 341 TitlebarBottomThickness(false) + 1) / 2; 342 343 window_icon_bounds_ = gfx::Rect(leading_button_start_ + kIconLeftSpacing, y, 344 size, size); 345 leading_button_start_ += size + kIconLeftSpacing; 346 minimum_size_for_buttons_ += size + kIconLeftSpacing; 347 } 348 349 if (should_show_icon) 350 window_icon_->SetBoundsRect(window_icon_bounds_); 351 352 if (window_title_) { 353 window_title_->SetVisible(should_show_title); 354 if (should_show_title) { 355 window_title_->SetText(delegate_->GetWindowTitle()); 356 357 int text_width = std::max( 358 0, host->width() - trailing_button_start_ - kTitleLogoSpacing - 359 leading_button_start_ - kIconTitleSpacing); 360 window_title_->SetBounds(leading_button_start_ + kIconTitleSpacing, 361 window_icon_bounds_.y(), 362 text_width, window_icon_bounds_.height()); 363 leading_button_start_ += text_width + kIconTitleSpacing; 364 } 365 } 366 367 if (use_hidden_icon_location) { 368 if (has_leading_buttons_) { 369 // There are window button icons on the left. Don't size the hidden window 370 // icon that people can double click on to close the window. 371 window_icon_bounds_ = gfx::Rect(); 372 } else { 373 // We set the icon bounds to a small rectangle in the top leading corner 374 // if there are no icons on the leading side. 375 window_icon_bounds_ = gfx::Rect( 376 frame_thickness + kIconLeftSpacing, frame_thickness, size, size); 377 } 378 } 379 } 380 381 void OpaqueBrowserFrameViewLayout::LayoutNewStyleAvatar(views::View* host) { 382 DCHECK(switches::IsNewAvatarMenu()); 383 if (!new_avatar_button_) 384 return; 385 386 int button_width = new_avatar_button_->GetPreferredSize().width(); 387 int button_width_with_offset = button_width + kNewAvatarButtonOffset; 388 389 int button_x = 390 host->width() - trailing_button_start_ - button_width_with_offset; 391 int button_y = CaptionButtonY(!IsTitleBarCondensed()); 392 393 minimum_size_for_buttons_ += button_width_with_offset; 394 trailing_button_start_ += button_width_with_offset; 395 396 // In non-maximized mode, allow the new tab button to completely slide under 397 // the avatar button. 398 if (!IsTitleBarCondensed()) { 399 trailing_button_start_ -= 400 TabStrip::kNewTabButtonAssetWidth + kNewTabCaptionNormalSpacing; 401 } 402 403 // Do not include the 1px padding that is added for the caption buttons. 404 new_avatar_button_->SetBounds( 405 button_x, button_y, button_width, kCaptionButtonHeightWithPadding - 1); 406 } 407 408 void OpaqueBrowserFrameViewLayout::LayoutAvatar(views::View* host) { 409 // Even though the avatar is used for both incognito and profiles we always 410 // use the incognito icon to layout the avatar button. The profile icon 411 // can be customized so we can't depend on its size to perform layout. 412 gfx::ImageSkia incognito_icon = delegate_->GetOTRAvatarIcon(); 413 414 bool avatar_on_right = ShouldAvatarBeOnRight(); 415 int avatar_bottom = GetTabStripInsetsTop(false) + 416 delegate_->GetTabStripHeight() - kAvatarBottomSpacing; 417 int avatar_restored_y = avatar_bottom - incognito_icon.height(); 418 int avatar_x = avatar_on_right ? 419 host->width() - trailing_button_start_ - kAvatarOuterSpacing - 420 incognito_icon.width() : 421 leading_button_start_ + kAvatarOuterSpacing; 422 int avatar_y = IsTitleBarCondensed() ? 423 (NonClientTopBorderHeight(false) + kTabstripTopShadowThickness) : 424 avatar_restored_y; 425 avatar_bounds_.SetRect( 426 avatar_x, 427 avatar_y, 428 incognito_icon.width(), 429 delegate_->ShouldShowAvatar() ? (avatar_bottom - avatar_y) : 0); 430 if (avatar_button_) { 431 avatar_button_->set_button_on_right(avatar_on_right); 432 avatar_button_->SetBoundsRect(avatar_bounds_); 433 434 int edge_offset; 435 if (avatar_label_) { 436 avatar_label_->SetLabelOnRight(avatar_on_right); 437 // Space between the bottom of the avatar and the bottom of the avatar 438 // label. 439 const int kAvatarLabelBottomSpacing = 3; 440 gfx::Size label_size = avatar_label_->GetPreferredSize(); 441 // The outside edge of the avatar label should be just outside that of the 442 // avatar menu button. 443 int avatar_label_x = avatar_on_right ? 444 (host->width() - trailing_button_start_ - label_size.width()) : 445 leading_button_start_; 446 gfx::Rect label_bounds( 447 avatar_label_x, 448 avatar_bottom - kAvatarLabelBottomSpacing - label_size.height(), 449 label_size.width(), 450 delegate_->ShouldShowAvatar() ? label_size.height() : 0); 451 avatar_label_->SetBoundsRect(label_bounds); 452 edge_offset = label_size.width(); 453 } else { 454 edge_offset = kAvatarOuterSpacing + incognito_icon.width(); 455 } 456 if (avatar_on_right) 457 trailing_button_start_ += edge_offset; 458 else 459 leading_button_start_ += edge_offset; 460 461 // We just add the avatar button size to the minimum size because clicking 462 // the avatar label does the same thing as clicking the avatar button. 463 minimum_size_for_buttons_ += kAvatarOuterSpacing + incognito_icon.width(); 464 } 465 } 466 467 void OpaqueBrowserFrameViewLayout::ConfigureButton( 468 views::View* host, 469 views::FrameButton button_id, 470 ButtonAlignment alignment, 471 int caption_y) { 472 switch (button_id) { 473 case views::FRAME_BUTTON_MINIMIZE: { 474 minimize_button_->SetVisible(true); 475 SetBoundsForButton(host, minimize_button_, alignment, caption_y); 476 break; 477 } 478 case views::FRAME_BUTTON_MAXIMIZE: { 479 // When the window is restored, we show a maximized button; otherwise, we 480 // show a restore button. 481 bool is_restored = !delegate_->IsMaximized() && !delegate_->IsMinimized(); 482 views::ImageButton* invisible_button = is_restored ? 483 restore_button_ : maximize_button_; 484 invisible_button->SetVisible(false); 485 486 views::ImageButton* visible_button = is_restored ? 487 maximize_button_ : restore_button_; 488 visible_button->SetVisible(true); 489 SetBoundsForButton(host, visible_button, alignment, caption_y); 490 break; 491 } 492 case views::FRAME_BUTTON_CLOSE: { 493 close_button_->SetVisible(true); 494 SetBoundsForButton(host, close_button_, alignment, caption_y); 495 break; 496 } 497 } 498 } 499 500 void OpaqueBrowserFrameViewLayout::HideButton(views::FrameButton button_id) { 501 switch (button_id) { 502 case views::FRAME_BUTTON_MINIMIZE: 503 minimize_button_->SetVisible(false); 504 break; 505 case views::FRAME_BUTTON_MAXIMIZE: 506 restore_button_->SetVisible(false); 507 maximize_button_->SetVisible(false); 508 break; 509 case views::FRAME_BUTTON_CLOSE: 510 close_button_->SetVisible(false); 511 break; 512 } 513 } 514 515 void OpaqueBrowserFrameViewLayout::SetBoundsForButton( 516 views::View* host, 517 views::ImageButton* button, 518 ButtonAlignment alignment, 519 int caption_y) { 520 gfx::Size button_size = button->GetPreferredSize(); 521 522 button->SetImageAlignment( 523 (alignment == ALIGN_LEADING) ? 524 views::ImageButton::ALIGN_RIGHT : views::ImageButton::ALIGN_LEFT, 525 views::ImageButton::ALIGN_BOTTOM); 526 527 // There should always be the same number of non-shadow pixels visible to the 528 // side of the caption buttons. In maximized mode we extend buttons to the 529 // screen top and the rightmost button to the screen right (or leftmost button 530 // to the screen left, for left-aligned buttons) to obey Fitts' Law. 531 bool title_bar_condensed = IsTitleBarCondensed(); 532 533 // When we are the first button on the leading side and are the close 534 // button, we must flip ourselves, because the close button assets have 535 // a little notch to fit in the rounded frame. 536 button->SetDrawImageMirrored(alignment == ALIGN_LEADING && 537 !has_leading_buttons_ && 538 button == close_button_); 539 // If the window is maximized, align the buttons to its upper edge. 540 int extra_height = title_bar_condensed ? extra_caption_y_ : 0; 541 542 switch (alignment) { 543 case ALIGN_LEADING: { 544 if (has_leading_buttons_) 545 leading_button_start_ += window_caption_spacing_; 546 547 // If we're the first button on the left and maximized, add width to the 548 // right hand side of the screen. 549 int extra_width = (title_bar_condensed && !has_leading_buttons_) ? 550 (kFrameBorderThickness - 551 views::NonClientFrameView::kFrameShadowThickness) : 0; 552 553 button->SetBounds( 554 leading_button_start_, 555 caption_y - extra_height, 556 button_size.width() + extra_width, 557 button_size.height() + extra_height); 558 559 leading_button_start_ += extra_width + button_size.width(); 560 minimum_size_for_buttons_ += extra_width + button_size.width(); 561 has_leading_buttons_ = true; 562 break; 563 } 564 case ALIGN_TRAILING: { 565 if (has_trailing_buttons_) 566 trailing_button_start_ += window_caption_spacing_; 567 568 // If we're the first button on the right and maximized, add width to the 569 // right hand side of the screen. 570 int extra_width = (title_bar_condensed && !has_trailing_buttons_) ? 571 (kFrameBorderThickness - 572 views::NonClientFrameView::kFrameShadowThickness) : 0; 573 574 button->SetBounds( 575 host->width() - trailing_button_start_ - extra_width - 576 button_size.width(), 577 caption_y - extra_height, 578 button_size.width() + extra_width, 579 button_size.height() + extra_height); 580 581 trailing_button_start_ += extra_width + button_size.width(); 582 minimum_size_for_buttons_ += extra_width + button_size.width(); 583 has_trailing_buttons_ = true; 584 break; 585 } 586 } 587 } 588 589 void OpaqueBrowserFrameViewLayout::SetView(int id, views::View* view) { 590 // Why do things this way instead of having an Init() method, where we're 591 // passed the views we'll handle? Because OpaqueBrowserFrameView doesn't own 592 // all the views which are part of it. The avatar stuff, for example, will be 593 // added and removed by the base class of OpaqueBrowserFrameView. 594 switch (id) { 595 case VIEW_ID_MINIMIZE_BUTTON: 596 if (view) { 597 DCHECK_EQ(std::string(views::ImageButton::kViewClassName), 598 view->GetClassName()); 599 } 600 minimize_button_ = static_cast<views::ImageButton*>(view); 601 break; 602 case VIEW_ID_MAXIMIZE_BUTTON: 603 if (view) { 604 DCHECK_EQ(std::string(views::ImageButton::kViewClassName), 605 view->GetClassName()); 606 } 607 maximize_button_ = static_cast<views::ImageButton*>(view); 608 break; 609 case VIEW_ID_RESTORE_BUTTON: 610 if (view) { 611 DCHECK_EQ(std::string(views::ImageButton::kViewClassName), 612 view->GetClassName()); 613 } 614 restore_button_ = static_cast<views::ImageButton*>(view); 615 break; 616 case VIEW_ID_CLOSE_BUTTON: 617 if (view) { 618 DCHECK_EQ(std::string(views::ImageButton::kViewClassName), 619 view->GetClassName()); 620 } 621 close_button_ = static_cast<views::ImageButton*>(view); 622 break; 623 case VIEW_ID_WINDOW_ICON: 624 window_icon_ = view; 625 break; 626 case VIEW_ID_WINDOW_TITLE: 627 if (view) { 628 DCHECK_EQ(std::string(views::Label::kViewClassName), 629 view->GetClassName()); 630 } 631 window_title_ = static_cast<views::Label*>(view); 632 break; 633 case VIEW_ID_AVATAR_LABEL: 634 avatar_label_ = static_cast<AvatarLabel*>(view); 635 break; 636 case VIEW_ID_AVATAR_BUTTON: 637 if (view) { 638 DCHECK_EQ(std::string(AvatarMenuButton::kViewClassName), 639 view->GetClassName()); 640 } 641 avatar_button_ = static_cast<AvatarMenuButton*>(view); 642 break; 643 case VIEW_ID_NEW_AVATAR_BUTTON: 644 new_avatar_button_ = view; 645 break; 646 default: 647 NOTIMPLEMENTED() << "Unknown view id " << id; 648 break; 649 } 650 } 651 652 /////////////////////////////////////////////////////////////////////////////// 653 // OpaqueBrowserFrameView, views::LayoutManager: 654 655 void OpaqueBrowserFrameViewLayout::Layout(views::View* host) { 656 // Reset all our data so that everything is invisible. 657 int thickness = FrameBorderThickness(false); 658 leading_button_start_ = thickness; 659 trailing_button_start_ = thickness; 660 minimum_size_for_buttons_ = leading_button_start_ + trailing_button_start_; 661 has_leading_buttons_ = false; 662 has_trailing_buttons_ = false; 663 664 LayoutWindowControls(host); 665 LayoutTitleBar(host); 666 667 // We now add a single pixel to the leading spacing. We do this because the 668 // avatar and tab strip start one pixel inward compared to where things start 669 // on the trailing side. 670 leading_button_start_++; 671 672 if (delegate_->IsRegularOrGuestSession() && switches::IsNewAvatarMenu()) 673 LayoutNewStyleAvatar(host); 674 else 675 LayoutAvatar(host); 676 677 client_view_bounds_ = CalculateClientAreaBounds( 678 host->width(), host->height()); 679 } 680 681 gfx::Size OpaqueBrowserFrameViewLayout::GetPreferredSize( 682 const views::View* host) const { 683 // This is never used; NonClientView::GetPreferredSize() will be called 684 // instead. 685 NOTREACHED(); 686 return gfx::Size(); 687 } 688 689 void OpaqueBrowserFrameViewLayout::ViewAdded(views::View* host, 690 views::View* view) { 691 SetView(view->id(), view); 692 } 693 694 void OpaqueBrowserFrameViewLayout::ViewRemoved(views::View* host, 695 views::View* view) { 696 SetView(view->id(), NULL); 697 } 698