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