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