Home | History | Annotate | Download | only in frame
      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