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
18namespace {
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.
22const int kNonClientRestoredExtraThickness = 9;
23
24// The titlebar never shrinks too short to show the caption button plus some
25// padding below it.
26const int kCaptionButtonHeightWithPadding = 19;
27
28// There is a 5 px gap between the title text and the caption buttons.
29const 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.
33const int kFrameBorderThickness = 4;
34
35// The titlebar has a 2 px 3D edge along the top and bottom.
36const int kTitlebarTopAndBottomEdgeThickness = 2;
37
38// The icon is inset 2 px from the left frame border.
39const int kIconLeftSpacing = 2;
40
41// There is a 4 px gap between the icon and the title text.
42const 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).
47const int kAvatarBottomSpacing = 2;
48
49// Space between the frame border and the edge of the avatar.
50const int kAvatarOuterSpacing = 2;
51
52// Space between the edge of the avatar and the tabstrip.
53const int kAvatarInnerSpacing = 4;
54
55// Space between the trailing edge of the avatar label and the tabstrip.
56const int kAvatarLabelInnerSpacing = 10;
57
58// How far the new avatar button is from the closest caption button.
59const 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.
65const 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.
70const 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.
75const 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.
79const 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.
83const 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.
88const int kExtraCaption = 2;
89
90// Default extra spacing between individual window caption buttons.
91const int kCaptionButtonSpacing = 2;
92#else
93const int kExtraCaption = 0;
94const int kCaptionButtonSpacing = 0;
95#endif
96
97}  // namespace
98
99///////////////////////////////////////////////////////////////////////////////
100// OpaqueBrowserFrameView, public:
101
102OpaqueBrowserFrameViewLayout::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
126OpaqueBrowserFrameViewLayout::~OpaqueBrowserFrameViewLayout() {}
127
128void 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
135gfx::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
158gfx::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
180gfx::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
190int OpaqueBrowserFrameViewLayout::FrameBorderThickness(bool restored) const {
191  return (!restored && (IsTitleBarCondensed() ||
192                        delegate_->IsFullscreen())) ?
193      0 : kFrameBorderThickness;
194}
195
196int 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
203int 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
219int OpaqueBrowserFrameViewLayout::GetTabStripInsetsTop(bool restored) const {
220  return NonClientTopBorderHeight(restored) + ((!restored &&
221      (!delegate_->ShouldLeaveOffsetNearTopBorder() ||
222      IsTitleBarCondensed() ||
223      delegate_->IsFullscreen())) ?
224      0 : kNonClientRestoredExtraThickness);
225}
226
227int OpaqueBrowserFrameViewLayout::TitlebarBottomThickness(bool restored) const {
228  return kTitlebarTopAndBottomEdgeThickness +
229      ((!restored && IsTitleBarCondensed()) ? 0 :
230       views::NonClientFrameView::kClientEdgeThickness);
231}
232
233int 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
242gfx::Rect OpaqueBrowserFrameViewLayout::IconBounds() const {
243  return window_icon_bounds_;
244}
245
246gfx::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
256bool 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
266bool 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
272int OpaqueBrowserFrameViewLayout::NewTabCaptionSpacing() const {
273  return has_trailing_buttons_
274             ? (IsTitleBarCondensed() ? kNewTabCaptionCondensedSpacing
275                                      : kNewTabCaptionNormalSpacing)
276             : kNewTabNoCaptionButtonsSpacing;
277}
278
279void 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
312void 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
381void 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
408void 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
467void 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
500void 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
515void 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
589void 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
655void 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
681gfx::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
689void OpaqueBrowserFrameViewLayout::ViewAdded(views::View* host,
690                                             views::View* view) {
691  SetView(view->id(), view);
692}
693
694void OpaqueBrowserFrameViewLayout::ViewRemoved(views::View* host,
695                                               views::View* view) {
696  SetView(view->id(), NULL);
697}
698