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