Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / frame / opaque_browser_frame_view_layout.cc
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
17 namespace {
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.
21 const int kNonClientRestoredExtraThickness = 9;
22
23 // The titlebar never shrinks too short to show the caption button plus some
24 // padding below it.
25 const int kCaptionButtonHeightWithPadding = 19;
26
27 // There is a 5 px gap between the title text and the caption buttons.
28 const 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.
32 const int kFrameBorderThickness = 4;
33
34 // The titlebar has a 2 px 3D edge along the top and bottom.
35 const int kTitlebarTopAndBottomEdgeThickness = 2;
36
37 // The icon is inset 2 px from the left frame border.
38 const int kIconLeftSpacing = 2;
39
40 // There is a 4 px gap between the icon and the title text.
41 const 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).
46 const int kAvatarBottomSpacing = 2;
47
48 // Space between the frame border and the edge of the avatar.
49 const int kAvatarOuterSpacing = 2;
50
51 // Space between the edge of the avatar and the tabstrip.
52 const int kAvatarInnerSpacing = 4;
53
54 // Space between the trailing edge of the avatar label and the tabstrip.
55 const int kAvatarLabelInnerSpacing = 10;
56
57 // How far the new avatar button is from the closest caption button.
58 const 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.
64 const 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.
69 const 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.
74 const 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.
78 const 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.
82 const 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.
87 const int kExtraCaption = 2;
88
89 // Default extra spacing between individual window caption buttons.
90 const int kCaptionButtonSpacing = 2;
91 #else
92 const int kExtraCaption = 0;
93 const int kCaptionButtonSpacing = 0;
94 #endif
95
96 }  // namespace
97
98 ///////////////////////////////////////////////////////////////////////////////
99 // OpaqueBrowserFrameView, public:
100
101 OpaqueBrowserFrameViewLayout::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
125 OpaqueBrowserFrameViewLayout::~OpaqueBrowserFrameViewLayout() {}
126
127 void 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
134 gfx::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
157 gfx::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
179 gfx::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
189 int OpaqueBrowserFrameViewLayout::FrameBorderThickness(bool restored) const {
190   return (!restored && (IsTitleBarCondensed() ||
191                         delegate_->IsFullscreen())) ?
192       0 : kFrameBorderThickness;
193 }
194
195 int 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
202 int 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
218 int OpaqueBrowserFrameViewLayout::GetTabStripInsetsTop(bool restored) const {
219   return NonClientTopBorderHeight(restored) + ((!restored &&
220       (!delegate_->ShouldLeaveOffsetNearTopBorder() ||
221       IsTitleBarCondensed() ||
222       delegate_->IsFullscreen())) ?
223       0 : kNonClientRestoredExtraThickness);
224 }
225
226 int OpaqueBrowserFrameViewLayout::TitlebarBottomThickness(bool restored) const {
227   return kTitlebarTopAndBottomEdgeThickness +
228       ((!restored && IsTitleBarCondensed()) ? 0 :
229        views::NonClientFrameView::kClientEdgeThickness);
230 }
231
232 int 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
241 gfx::Rect OpaqueBrowserFrameViewLayout::IconBounds() const {
242   return window_icon_bounds_;
243 }
244
245 gfx::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
255 bool 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
265 bool 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
271 int OpaqueBrowserFrameViewLayout::NewTabCaptionSpacing() const {
272   return has_trailing_buttons_
273              ? (IsTitleBarCondensed() ? kNewTabCaptionCondensedSpacing
274                                       : kNewTabCaptionNormalSpacing)
275              : kNewTabNoCaptionButtonsSpacing;
276 }
277
278 void 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
311 void 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() && window_icon_;
317   bool should_show_title = delegate_->ShouldShowWindowTitle() && window_title_;
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
380 void 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(!IsTitleBarCondensed());
391
392   trailing_button_start_ += button_size_with_offset;
393   minimum_size_for_buttons_ += button_size_with_offset;
394
395   // Do not include the 1px padding that is added for the caption buttons.
396   new_avatar_button_->SetBounds(button_x,
397                                 button_y,
398                                 label_size.width(),
399                                 kCaptionButtonHeightWithPadding - 1);
400 }
401
402 void OpaqueBrowserFrameViewLayout::LayoutAvatar(views::View* host) {
403   // Even though the avatar is used for both incognito and profiles we always
404   // use the incognito icon to layout the avatar button. The profile icon
405   // can be customized so we can't depend on its size to perform layout.
406   gfx::ImageSkia incognito_icon = delegate_->GetOTRAvatarIcon();
407
408   bool avatar_on_right = ShouldAvatarBeOnRight();
409   int avatar_bottom = GetTabStripInsetsTop(false) +
410       delegate_->GetTabStripHeight() - kAvatarBottomSpacing;
411   int avatar_restored_y = avatar_bottom - incognito_icon.height();
412   int avatar_x = avatar_on_right ?
413       host->width() - trailing_button_start_ - kAvatarOuterSpacing -
414           incognito_icon.width() :
415       leading_button_start_ + kAvatarOuterSpacing;
416   int avatar_y = IsTitleBarCondensed() ?
417       (NonClientTopBorderHeight(false) + kTabstripTopShadowThickness) :
418       avatar_restored_y;
419   avatar_bounds_.SetRect(
420       avatar_x,
421       avatar_y,
422       incognito_icon.width(),
423       delegate_->ShouldShowAvatar() ? (avatar_bottom - avatar_y) : 0);
424   if (avatar_button_) {
425     avatar_button_->set_button_on_right(avatar_on_right);
426     avatar_button_->SetBoundsRect(avatar_bounds_);
427
428     int edge_offset;
429     if (avatar_label_) {
430       avatar_label_->SetLabelOnRight(avatar_on_right);
431       // Space between the bottom of the avatar and the bottom of the avatar
432       // label.
433       const int kAvatarLabelBottomSpacing = 3;
434       gfx::Size label_size = avatar_label_->GetPreferredSize();
435       // The outside edge of the avatar label should be just outside that of the
436       // avatar menu button.
437       int avatar_label_x = avatar_on_right ?
438           (host->width() - trailing_button_start_ - label_size.width()) :
439           leading_button_start_;
440       gfx::Rect label_bounds(
441           avatar_label_x,
442           avatar_bottom - kAvatarLabelBottomSpacing - label_size.height(),
443           label_size.width(),
444           delegate_->ShouldShowAvatar() ? label_size.height() : 0);
445       avatar_label_->SetBoundsRect(label_bounds);
446       edge_offset = label_size.width();
447     } else {
448       edge_offset = kAvatarOuterSpacing + incognito_icon.width();
449     }
450     if (avatar_on_right)
451       trailing_button_start_ += edge_offset;
452     else
453       leading_button_start_ += edge_offset;
454
455     // We just add the avatar button size to the minimum size because clicking
456     // the avatar label does the same thing as clicking the avatar button.
457     minimum_size_for_buttons_ += kAvatarOuterSpacing + incognito_icon.width();
458   }
459 }
460
461 void OpaqueBrowserFrameViewLayout::ConfigureButton(
462     views::View* host,
463     views::FrameButton button_id,
464     ButtonAlignment alignment,
465     int caption_y) {
466   switch (button_id) {
467     case views::FRAME_BUTTON_MINIMIZE: {
468       minimize_button_->SetVisible(true);
469       SetBoundsForButton(host, minimize_button_, alignment, caption_y);
470       break;
471     }
472     case views::FRAME_BUTTON_MAXIMIZE: {
473       // When the window is restored, we show a maximized button; otherwise, we
474       // show a restore button.
475       bool is_restored = !delegate_->IsMaximized() && !delegate_->IsMinimized();
476       views::ImageButton* invisible_button = is_restored ?
477           restore_button_ : maximize_button_;
478       invisible_button->SetVisible(false);
479
480       views::ImageButton* visible_button = is_restored ?
481           maximize_button_ : restore_button_;
482       visible_button->SetVisible(true);
483       SetBoundsForButton(host, visible_button, alignment, caption_y);
484       break;
485     }
486     case views::FRAME_BUTTON_CLOSE: {
487       close_button_->SetVisible(true);
488       SetBoundsForButton(host, close_button_, alignment, caption_y);
489       break;
490     }
491   }
492 }
493
494 void OpaqueBrowserFrameViewLayout::HideButton(views::FrameButton button_id) {
495   switch (button_id) {
496     case views::FRAME_BUTTON_MINIMIZE:
497       minimize_button_->SetVisible(false);
498       break;
499     case views::FRAME_BUTTON_MAXIMIZE:
500       restore_button_->SetVisible(false);
501       maximize_button_->SetVisible(false);
502       break;
503     case views::FRAME_BUTTON_CLOSE:
504       close_button_->SetVisible(false);
505       break;
506   }
507 }
508
509 void OpaqueBrowserFrameViewLayout::SetBoundsForButton(
510     views::View* host,
511     views::ImageButton* button,
512     ButtonAlignment alignment,
513     int caption_y) {
514   gfx::Size button_size = button->GetPreferredSize();
515
516   button->SetImageAlignment(
517       (alignment == ALIGN_LEADING)  ?
518           views::ImageButton::ALIGN_RIGHT : views::ImageButton::ALIGN_LEFT,
519       views::ImageButton::ALIGN_BOTTOM);
520
521   // There should always be the same number of non-shadow pixels visible to the
522   // side of the caption buttons.  In maximized mode we extend buttons to the
523   // screen top and the rightmost button to the screen right (or leftmost button
524   // to the screen left, for left-aligned buttons) to obey Fitts' Law.
525   bool title_bar_condensed = IsTitleBarCondensed();
526
527   // When we are the first button on the leading side and are the close
528   // button, we must flip ourselves, because the close button assets have
529   // a little notch to fit in the rounded frame.
530   button->SetDrawImageMirrored(alignment == ALIGN_LEADING &&
531                                !has_leading_buttons_ &&
532                                button == close_button_);
533   // If the window is maximized, align the buttons to its upper edge.
534   int extra_height = title_bar_condensed ? extra_caption_y_ : 0;
535
536   switch (alignment) {
537     case ALIGN_LEADING: {
538       if (has_leading_buttons_)
539         leading_button_start_ += window_caption_spacing_;
540
541       // If we're the first button on the left and maximized, add width to the
542       // right hand side of the screen.
543       int extra_width = (title_bar_condensed && !has_leading_buttons_) ?
544         (kFrameBorderThickness -
545          views::NonClientFrameView::kFrameShadowThickness) : 0;
546
547       button->SetBounds(
548           leading_button_start_,
549           caption_y - extra_height,
550           button_size.width() + extra_width,
551           button_size.height() + extra_height);
552
553       leading_button_start_ += extra_width + button_size.width();
554       minimum_size_for_buttons_ += extra_width + button_size.width();
555       has_leading_buttons_ = true;
556       break;
557     }
558     case ALIGN_TRAILING: {
559       if (has_trailing_buttons_)
560         trailing_button_start_ += window_caption_spacing_;
561
562       // If we're the first button on the right and maximized, add width to the
563       // right hand side of the screen.
564       int extra_width = (title_bar_condensed && !has_trailing_buttons_) ?
565         (kFrameBorderThickness -
566          views::NonClientFrameView::kFrameShadowThickness) : 0;
567
568       button->SetBounds(
569           host->width() - trailing_button_start_ - extra_width -
570               button_size.width(),
571           caption_y - extra_height,
572           button_size.width() + extra_width,
573           button_size.height() + extra_height);
574
575       trailing_button_start_ += extra_width + button_size.width();
576       minimum_size_for_buttons_ += extra_width + button_size.width();
577       has_trailing_buttons_ = true;
578       break;
579     }
580   }
581 }
582
583 void OpaqueBrowserFrameViewLayout::SetView(int id, views::View* view) {
584   // Why do things this way instead of having an Init() method, where we're
585   // passed the views we'll handle? Because OpaqueBrowserFrameView doesn't own
586   // all the views which are part of it. The avatar stuff, for example, will be
587   // added and removed by the base class of OpaqueBrowserFrameView.
588   switch (id) {
589     case VIEW_ID_MINIMIZE_BUTTON:
590       if (view) {
591         DCHECK_EQ(std::string(views::ImageButton::kViewClassName),
592                   view->GetClassName());
593       }
594       minimize_button_ = static_cast<views::ImageButton*>(view);
595       break;
596     case VIEW_ID_MAXIMIZE_BUTTON:
597       if (view) {
598         DCHECK_EQ(std::string(views::ImageButton::kViewClassName),
599                   view->GetClassName());
600       }
601       maximize_button_ = static_cast<views::ImageButton*>(view);
602       break;
603     case VIEW_ID_RESTORE_BUTTON:
604       if (view) {
605         DCHECK_EQ(std::string(views::ImageButton::kViewClassName),
606                   view->GetClassName());
607       }
608       restore_button_ = static_cast<views::ImageButton*>(view);
609       break;
610     case VIEW_ID_CLOSE_BUTTON:
611       if (view) {
612         DCHECK_EQ(std::string(views::ImageButton::kViewClassName),
613                   view->GetClassName());
614       }
615       close_button_ = static_cast<views::ImageButton*>(view);
616       break;
617     case VIEW_ID_WINDOW_ICON:
618       window_icon_ = view;
619       break;
620     case VIEW_ID_WINDOW_TITLE:
621       if (view) {
622         DCHECK_EQ(std::string(views::Label::kViewClassName),
623                   view->GetClassName());
624       }
625       window_title_ = static_cast<views::Label*>(view);
626       break;
627     case VIEW_ID_AVATAR_LABEL:
628       avatar_label_ = static_cast<AvatarLabel*>(view);
629       break;
630     case VIEW_ID_AVATAR_BUTTON:
631       if (view) {
632         DCHECK_EQ(std::string(AvatarMenuButton::kViewClassName),
633                   view->GetClassName());
634       }
635       avatar_button_ = static_cast<AvatarMenuButton*>(view);
636       break;
637     case VIEW_ID_NEW_AVATAR_BUTTON:
638       new_avatar_button_ = view;
639       break;
640     default:
641       NOTIMPLEMENTED() << "Unknown view id " << id;
642       break;
643   }
644 }
645
646 ///////////////////////////////////////////////////////////////////////////////
647 // OpaqueBrowserFrameView, views::LayoutManager:
648
649 void OpaqueBrowserFrameViewLayout::Layout(views::View* host) {
650   // Reset all our data so that everything is invisible.
651   int thickness = FrameBorderThickness(false);
652   leading_button_start_ = thickness;
653   trailing_button_start_ = thickness;
654   minimum_size_for_buttons_ = leading_button_start_ + trailing_button_start_;
655   has_leading_buttons_ = false;
656   has_trailing_buttons_ = false;
657
658   LayoutWindowControls(host);
659   LayoutTitleBar(host);
660
661   // We now add a single pixel to the leading spacing. We do this because the
662   // avatar and tab strip start one pixel inward compared to where things start
663   // on the trailing side.
664   leading_button_start_++;
665
666   if (delegate_->IsRegularOrGuestSession() && switches::IsNewAvatarMenu())
667     LayoutNewStyleAvatar(host);
668   else
669     LayoutAvatar(host);
670
671   client_view_bounds_ = CalculateClientAreaBounds(
672       host->width(), host->height());
673 }
674
675 gfx::Size OpaqueBrowserFrameViewLayout::GetPreferredSize(
676     const views::View* host) const {
677   // This is never used; NonClientView::GetPreferredSize() will be called
678   // instead.
679   NOTREACHED();
680   return gfx::Size();
681 }
682
683 void OpaqueBrowserFrameViewLayout::ViewAdded(views::View* host,
684                                              views::View* view) {
685   SetView(view->id(), view);
686 }
687
688 void OpaqueBrowserFrameViewLayout::ViewRemoved(views::View* host,
689                                                views::View* view) {
690   SetView(view->id(), NULL);
691 }