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