opaque_browser_frame_view.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com// Use of this source code is governed by a BSD-style license that can be
3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com// found in the LICENSE file.
4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com
5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com#include "chrome/browser/ui/views/frame/opaque_browser_frame_view.h"
6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com
7ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com#include <algorithm>
8ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com#include <string>
99c970453fd1cb6e9618e37c61507465772deca80reed@android.com
109c970453fd1cb6e9618e37c61507465772deca80reed@android.com#include "base/command_line.h"
119c970453fd1cb6e9618e37c61507465772deca80reed@android.com#include "base/compiler_specific.h"
129c970453fd1cb6e9618e37c61507465772deca80reed@android.com#include "base/prefs/pref_service.h"
139c970453fd1cb6e9618e37c61507465772deca80reed@android.com#include "base/utf_string_conversions.h"
149c970453fd1cb6e9618e37c61507465772deca80reed@android.com#include "chrome/browser/themes/theme_properties.h"
159c970453fd1cb6e9618e37c61507465772deca80reed@android.com#include "chrome/browser/ui/views/avatar_menu_button.h"
169c970453fd1cb6e9618e37c61507465772deca80reed@android.com#include "chrome/browser/ui/views/frame/browser_frame.h"
179c970453fd1cb6e9618e37c61507465772deca80reed@android.com#include "chrome/browser/ui/views/frame/browser_view.h"
18f910d3b23bcf590ee937628dbab8e39a98ee5860bsalomon@google.com#include "chrome/browser/ui/views/tab_icon_view.h"
199c970453fd1cb6e9618e37c61507465772deca80reed@android.com#include "chrome/browser/ui/views/tabs/tab_strip.h"
209c970453fd1cb6e9618e37c61507465772deca80reed@android.com#include "chrome/browser/ui/views/toolbar_view.h"
21#include "chrome/common/chrome_notification_types.h"
22#include "chrome/common/chrome_switches.h"
23#include "chrome/common/pref_names.h"
24#include "content/public/browser/notification_service.h"
25#include "content/public/browser/web_contents.h"
26#include "grit/chromium_strings.h"
27#include "grit/generated_resources.h"
28#include "grit/theme_resources.h"
29#include "grit/ui_resources.h"
30#include "ui/base/accessibility/accessible_view_state.h"
31#include "ui/base/hit_test.h"
32#include "ui/base/l10n/l10n_util.h"
33#include "ui/base/resource/resource_bundle.h"
34#include "ui/base/theme_provider.h"
35#include "ui/gfx/canvas.h"
36#include "ui/gfx/font.h"
37#include "ui/gfx/image/image.h"
38#include "ui/gfx/image/image_skia.h"
39#include "ui/gfx/path.h"
40#include "ui/views/controls/button/image_button.h"
41#include "ui/views/controls/image_view.h"
42#include "ui/views/controls/label.h"
43#include "ui/views/widget/root_view.h"
44#include "ui/views/window/frame_background.h"
45#include "ui/views/window/window_shape.h"
46
47#if defined(OS_WIN)
48#include "win8/util/win8_util.h"
49#endif  // OS_WIN
50
51using content::WebContents;
52
53namespace {
54
55// The frame border is only visible in restored mode and is hardcoded to 4 px on
56// each side regardless of the system window border size.
57const int kFrameBorderThickness = 4;
58// Besides the frame border, there's another 9 px of empty space atop the
59// window in restored mode, to use to drag the window around.
60const int kNonClientRestoredExtraThickness = 9;
61// While resize areas on Windows are normally the same size as the window
62// borders, our top area is shrunk by 1 px to make it easier to move the window
63// around with our thinner top grabbable strip.  (Incidentally, our side and
64// bottom resize areas don't match the frame border thickness either -- they
65// span the whole nonclient area, so there's no "dead zone" for the mouse.)
66const int kTopResizeAdjust = 1;
67// In the window corners, the resize areas don't actually expand bigger, but the
68// 16 px at the end of each edge triggers diagonal resizing.
69const int kResizeAreaCornerSize = 16;
70// The titlebar never shrinks too short to show the caption button plus some
71// padding below it.
72const int kCaptionButtonHeightWithPadding = 19;
73// The content left/right images have a shadow built into them.
74const int kContentEdgeShadowThickness = 2;
75// The titlebar has a 2 px 3D edge along the top and bottom.
76const int kTitlebarTopAndBottomEdgeThickness = 2;
77// The icon is inset 2 px from the left frame border.
78const int kIconLeftSpacing = 2;
79// The icon never shrinks below 16 px on a side.
80const int kIconMinimumSize = 16;
81// There is a 4 px gap between the icon and the title text.
82const int kIconTitleSpacing = 4;
83// There is a 5 px gap between the title text and the caption buttons.
84const int kTitleLogoSpacing = 5;
85// The avatar ends 2 px above the bottom of the tabstrip (which, given the
86// way the tabstrip draws its bottom edge, will appear like a 1 px gap to the
87// user).
88const int kAvatarBottomSpacing = 2;
89// Space between the frame border and the left edge of the avatar.
90const int kAvatarLeftSpacing = 2;
91// Space between the right edge of the avatar and the tabstrip.
92const int kAvatarRightSpacing = -2;
93// The top 3 px of the tabstrip is shadow; in maximized mode we push this off
94// the top of the screen so the tabs appear flush against the screen edge.
95const int kTabstripTopShadowThickness = 3;
96// In restored mode, the New Tab button isn't at the same height as the caption
97// buttons, but the space will look cluttered if it actually slides under them,
98// so we stop it when the gap between the two is down to 5 px.
99const int kNewTabCaptionRestoredSpacing = 5;
100// In maximized mode, where the New Tab button and the caption buttons are at
101// similar vertical coordinates, we need to reserve a larger, 16 px gap to avoid
102// looking too cluttered.
103const int kNewTabCaptionMaximizedSpacing = 16;
104// How far to indent the tabstrip from the left side of the screen when there
105// is no avatar icon.
106const int kTabStripIndent = -6;
107
108// Converts |bounds| from |src|'s coordinate system to |dst|, and checks if
109// |pt| is contained within.
110bool ConvertedContainsCheck(gfx::Rect bounds, const views::View* src,
111                            const views::View* dst, const gfx::Point& pt) {
112  DCHECK(src);
113  DCHECK(dst);
114  gfx::Point origin(bounds.origin());
115  views::View::ConvertPointToTarget(src, dst, &origin);
116  bounds.set_origin(origin);
117  return bounds.Contains(pt);
118}
119
120bool ShouldAddDefaultCaptionButtons() {
121#if defined(OS_WIN)
122  return !win8::IsSingleWindowMetroMode();
123#endif  // OS_WIN
124  return true;
125}
126
127}  // namespace
128
129///////////////////////////////////////////////////////////////////////////////
130// OpaqueBrowserFrameView, public:
131
132OpaqueBrowserFrameView::OpaqueBrowserFrameView(BrowserFrame* frame,
133                                               BrowserView* browser_view)
134    : BrowserNonClientFrameView(frame, browser_view),
135      minimize_button_(NULL),
136      maximize_button_(NULL),
137      restore_button_(NULL),
138      close_button_(NULL),
139      window_icon_(NULL),
140      window_title_(NULL),
141      frame_background_(new views::FrameBackground()) {
142  if (ShouldAddDefaultCaptionButtons()) {
143    minimize_button_ = InitWindowCaptionButton(IDR_MINIMIZE,
144                                               IDR_MINIMIZE_H,
145                                               IDR_MINIMIZE_P,
146                                               IDR_MINIMIZE_BUTTON_MASK,
147                                               IDS_ACCNAME_MINIMIZE);
148    maximize_button_ = InitWindowCaptionButton(IDR_MAXIMIZE,
149                                               IDR_MAXIMIZE_H,
150                                               IDR_MAXIMIZE_P,
151                                               IDR_MAXIMIZE_BUTTON_MASK,
152                                               IDS_ACCNAME_MAXIMIZE);
153    restore_button_ = InitWindowCaptionButton(IDR_RESTORE,
154                                              IDR_RESTORE_H,
155                                              IDR_RESTORE_P,
156                                              IDR_RESTORE_BUTTON_MASK,
157                                              IDS_ACCNAME_RESTORE);
158    close_button_ = InitWindowCaptionButton(IDR_CLOSE,
159                                            IDR_CLOSE_H,
160                                            IDR_CLOSE_P,
161                                            IDR_CLOSE_BUTTON_MASK,
162                                            IDS_ACCNAME_CLOSE);
163  }
164
165  // Initializing the TabIconView is expensive, so only do it if we need to.
166  if (browser_view->ShouldShowWindowIcon()) {
167    window_icon_ = new TabIconView(this);
168    window_icon_->set_is_light(true);
169    AddChildView(window_icon_);
170    window_icon_->Update();
171  }
172
173  window_title_ = new views::Label(browser_view->GetWindowTitle(),
174                                   BrowserFrame::GetTitleFont());
175  window_title_->SetVisible(browser_view->ShouldShowWindowTitle());
176  window_title_->SetEnabledColor(SK_ColorWHITE);
177  // TODO(msw): Use a transparent background color as a workaround to use the
178  // gfx::Canvas::NO_SUBPIXEL_RENDERING flag and avoid some visual artifacts.
179  window_title_->SetBackgroundColor(0x00000000);
180  window_title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
181  AddChildView(window_title_);
182
183  UpdateAvatarInfo();
184  if (!browser_view->IsOffTheRecord()) {
185    registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED,
186                   content::NotificationService::AllSources());
187  }
188}
189
190OpaqueBrowserFrameView::~OpaqueBrowserFrameView() {
191}
192
193///////////////////////////////////////////////////////////////////////////////
194// OpaqueBrowserFrameView, protected:
195
196int OpaqueBrowserFrameView::GetReservedHeight() const {
197  return 0;
198}
199
200gfx::Rect OpaqueBrowserFrameView::GetBoundsForReservedArea() const {
201  gfx::Rect client_view_bounds = CalculateClientAreaBounds(width(), height());
202  return gfx::Rect(
203      client_view_bounds.x(),
204      client_view_bounds.y() + client_view_bounds.height(),
205      client_view_bounds.width(),
206      GetReservedHeight());
207}
208
209int OpaqueBrowserFrameView::NonClientTopBorderHeight(
210    bool restored) const {
211  views::WidgetDelegate* delegate = frame()->widget_delegate();
212  // |delegate| may be NULL if called from callback of InputMethodChanged while
213  // a window is being destroyed.
214  // See more discussion at http://crosbug.com/8958
215  if (delegate && delegate->ShouldShowWindowTitle()) {
216    return std::max(FrameBorderThickness(restored) + IconSize(),
217        CaptionButtonY(restored) + kCaptionButtonHeightWithPadding) +
218        TitlebarBottomThickness(restored);
219  }
220
221  return FrameBorderThickness(restored) -
222      ((browser_view()->IsTabStripVisible() &&
223          !restored && !frame()->ShouldLeaveOffsetNearTopBorder())
224              ? kTabstripTopShadowThickness : 0);
225}
226
227///////////////////////////////////////////////////////////////////////////////
228// OpaqueBrowserFrameView, BrowserNonClientFrameView implementation:
229
230gfx::Rect OpaqueBrowserFrameView::GetBoundsForTabStrip(
231    views::View* tabstrip) const {
232  if (!tabstrip)
233    return gfx::Rect();
234
235  gfx::Rect bounds = GetBoundsForTabStripAndAvatarArea(tabstrip);
236  const int space_left_of_tabstrip = browser_view()->ShouldShowAvatar() ?
237      (kAvatarLeftSpacing + avatar_bounds_.width() + kAvatarRightSpacing) :
238      kTabStripIndent;
239  bounds.Inset(space_left_of_tabstrip, 0, 0, 0);
240  return bounds;
241}
242
243BrowserNonClientFrameView::TabStripInsets
244OpaqueBrowserFrameView::GetTabStripInsets(bool restored) const {
245  int top = NonClientTopBorderHeight(restored) + ((!restored &&
246      (!frame()->ShouldLeaveOffsetNearTopBorder() ||
247      frame()->IsFullscreen())) ?
248      0 : kNonClientRestoredExtraThickness);
249  // TODO: include OTR and caption.
250  return TabStripInsets(top, 0, 0);
251}
252
253int OpaqueBrowserFrameView::GetThemeBackgroundXInset() const {
254  return 0;
255}
256
257void OpaqueBrowserFrameView::UpdateThrobber(bool running) {
258  if (window_icon_)
259    window_icon_->Update();
260}
261
262gfx::Size OpaqueBrowserFrameView::GetMinimumSize() {
263  gfx::Size min_size(browser_view()->GetMinimumSize());
264  int border_thickness = NonClientBorderThickness();
265  min_size.Enlarge(2 * border_thickness,
266                   NonClientTopBorderHeight(false) + border_thickness);
267
268  views::WidgetDelegate* delegate = frame()->widget_delegate();
269  int min_titlebar_width = (2 * FrameBorderThickness(false)) +
270      kIconLeftSpacing +
271      (delegate && delegate->ShouldShowWindowIcon() ?
272       (IconSize() + kTitleLogoSpacing) : 0);
273#if !defined(OS_CHROMEOS)
274  if (ShouldAddDefaultCaptionButtons()) {
275    min_titlebar_width +=
276        minimize_button_->GetMinimumSize().width() +
277        restore_button_->GetMinimumSize().width() +
278        close_button_->GetMinimumSize().width();
279  }
280#endif
281  min_size.set_width(std::max(min_size.width(), min_titlebar_width));
282
283  // Ensure that the minimum width is enough to hold a minimum width tab strip
284  // and avatar icon at their usual insets.
285  if (browser_view()->IsTabStripVisible()) {
286    TabStrip* tabstrip = browser_view()->tabstrip();
287    const int min_tabstrip_width = tabstrip->GetMinimumSize().width();
288    const int min_tabstrip_area_width =
289        width() - GetBoundsForTabStripAndAvatarArea(tabstrip).width() +
290        min_tabstrip_width + browser_view()->GetOTRAvatarIcon().width() +
291        kAvatarLeftSpacing + kAvatarRightSpacing;
292    min_size.set_width(std::max(min_size.width(), min_tabstrip_area_width));
293  }
294
295  return min_size;
296}
297
298///////////////////////////////////////////////////////////////////////////////
299// OpaqueBrowserFrameView, views::NonClientFrameView implementation:
300
301gfx::Rect OpaqueBrowserFrameView::GetBoundsForClientView() const {
302  return client_view_bounds_;
303}
304
305gfx::Rect OpaqueBrowserFrameView::GetWindowBoundsForClientBounds(
306    const gfx::Rect& client_bounds) const {
307  int top_height = NonClientTopBorderHeight(false);
308  int border_thickness = NonClientBorderThickness();
309  return gfx::Rect(std::max(0, client_bounds.x() - border_thickness),
310                   std::max(0, client_bounds.y() - top_height),
311                   client_bounds.width() + (2 * border_thickness),
312                   client_bounds.height() + top_height + border_thickness);
313}
314
315int OpaqueBrowserFrameView::NonClientHitTest(const gfx::Point& point) {
316  if (!bounds().Contains(point))
317    return HTNOWHERE;
318
319  // See if the point is within the avatar menu button.
320  if (avatar_button() &&
321      avatar_button()->GetMirroredBounds().Contains(point))
322    return HTCLIENT;
323
324  int frame_component = frame()->client_view()->NonClientHitTest(point);
325
326  // See if we're in the sysmenu region.  We still have to check the tabstrip
327  // first so that clicks in a tab don't get treated as sysmenu clicks.
328  gfx::Rect sysmenu_rect(IconBounds());
329  // In maximized mode we extend the rect to the screen corner to take advantage
330  // of Fitts' Law.
331  if (frame()->IsMaximized())
332    sysmenu_rect.SetRect(0, 0, sysmenu_rect.right(), sysmenu_rect.bottom());
333  sysmenu_rect.set_x(GetMirroredXForRect(sysmenu_rect));
334  if (sysmenu_rect.Contains(point))
335    return (frame_component == HTCLIENT) ? HTCLIENT : HTSYSMENU;
336
337  if (frame_component != HTNOWHERE)
338    return frame_component;
339
340  // Then see if the point is within any of the window controls.
341  if (close_button_ && close_button_->visible() &&
342      close_button_->GetMirroredBounds().Contains(point))
343    return HTCLOSE;
344  if (restore_button_ && restore_button_->visible() &&
345      restore_button_->GetMirroredBounds().Contains(point))
346    return HTMAXBUTTON;
347  if (maximize_button_ && maximize_button_->visible() &&
348      maximize_button_->GetMirroredBounds().Contains(point))
349    return HTMAXBUTTON;
350  if (minimize_button_ && minimize_button_->visible() &&
351      minimize_button_->GetMirroredBounds().Contains(point))
352    return HTMINBUTTON;
353
354  views::WidgetDelegate* delegate = frame()->widget_delegate();
355  if (!delegate) {
356    LOG(WARNING) << "delegate is NULL, returning safe default.";
357    return HTCAPTION;
358  }
359  int window_component = GetHTComponentForFrame(point, TopResizeHeight(),
360      NonClientBorderThickness(), kResizeAreaCornerSize, kResizeAreaCornerSize,
361      delegate->CanResize());
362  // Fall back to the caption if no other component matches.
363  return (window_component == HTNOWHERE) ? HTCAPTION : window_component;
364}
365
366void OpaqueBrowserFrameView::GetWindowMask(const gfx::Size& size,
367                                           gfx::Path* window_mask) {
368  DCHECK(window_mask);
369
370  if (frame()->IsMaximized() || frame()->IsFullscreen())
371    return;
372
373  views::GetDefaultWindowMask(size, window_mask);
374}
375
376void OpaqueBrowserFrameView::ResetWindowControls() {
377  if (!ShouldAddDefaultCaptionButtons())
378    return;
379  restore_button_->SetState(views::CustomButton::STATE_NORMAL);
380  minimize_button_->SetState(views::CustomButton::STATE_NORMAL);
381  maximize_button_->SetState(views::CustomButton::STATE_NORMAL);
382  // The close button isn't affected by this constraint.
383}
384
385void OpaqueBrowserFrameView::UpdateWindowIcon() {
386  window_icon_->SchedulePaint();
387}
388
389void OpaqueBrowserFrameView::UpdateWindowTitle() {
390  if (!frame()->IsFullscreen())
391    window_title_->SchedulePaint();
392}
393
394///////////////////////////////////////////////////////////////////////////////
395// OpaqueBrowserFrameView, views::View overrides:
396
397void OpaqueBrowserFrameView::OnPaint(gfx::Canvas* canvas) {
398  if (frame()->IsFullscreen())
399    return;  // Nothing is visible, so don't bother to paint.
400
401  if (frame()->IsMaximized())
402    PaintMaximizedFrameBorder(canvas);
403  else
404    PaintRestoredFrameBorder(canvas);
405
406  // The window icon and title are painted by their respective views.
407  /* TODO(pkasting):  If this window is active, we should also draw a drop
408   * shadow on the title.  This is tricky, because we don't want to hardcode a
409   * shadow color (since we want to work with various themes), but we can't
410   * alpha-blend either (since the Windows text APIs don't really do this).
411   * So we'd need to sample the background color at the right location and
412   * synthesize a good shadow color. */
413
414  if (browser_view()->IsToolbarVisible())
415    PaintToolbarBackground(canvas);
416  if (!frame()->IsMaximized())
417    PaintRestoredClientEdge(canvas);
418}
419
420void OpaqueBrowserFrameView::Layout() {
421  LayoutWindowControls();
422  LayoutTitleBar();
423  LayoutAvatar();
424  client_view_bounds_ = CalculateClientAreaBounds(width(), height());
425}
426
427bool OpaqueBrowserFrameView::HitTestRect(const gfx::Rect& rect) const {
428  // If |rect| does not intersect the bounds of the client area, claim it.
429  bool in_nonclient = NonClientFrameView::HitTestRect(rect);
430  if (in_nonclient)
431    return in_nonclient;
432
433  // Otherwise claim it only if it's in a non-tab portion of the tabstrip.
434  if (!browser_view()->tabstrip())
435    return false;
436  gfx::Rect tabstrip_bounds(browser_view()->tabstrip()->bounds());
437  gfx::Point tabstrip_origin(tabstrip_bounds.origin());
438  View::ConvertPointToTarget(frame()->client_view(), this, &tabstrip_origin);
439  tabstrip_bounds.set_origin(tabstrip_origin);
440  if (rect.bottom() > tabstrip_bounds.bottom())
441    return false;
442
443  // We convert from our parent's coordinates since we assume we fill its bounds
444  // completely. We need to do this since we're not a parent of the tabstrip,
445  // meaning ConvertPointToTarget would otherwise return something bogus.
446  // TODO(tdanderson): Initialize |browser_view_point| using |rect| instead of
447  // its center point once GetEventHandlerForRect() is implemented.
448  gfx::Point browser_view_point(rect.CenterPoint());
449  View::ConvertPointToTarget(parent(), browser_view(), &browser_view_point);
450  return browser_view()->IsPositionInWindowCaption(browser_view_point);
451}
452
453void OpaqueBrowserFrameView::GetAccessibleState(
454    ui::AccessibleViewState* state) {
455  state->role = ui::AccessibilityTypes::ROLE_TITLEBAR;
456}
457
458///////////////////////////////////////////////////////////////////////////////
459// OpaqueBrowserFrameView, views::ButtonListener implementation:
460
461void OpaqueBrowserFrameView::ButtonPressed(views::Button* sender,
462                                           const ui::Event& event) {
463  if (sender == minimize_button_)
464    frame()->Minimize();
465  else if (sender == maximize_button_)
466    frame()->Maximize();
467  else if (sender == restore_button_)
468    frame()->Restore();
469  else if (sender == close_button_)
470    frame()->Close();
471}
472
473///////////////////////////////////////////////////////////////////////////////
474// OpaqueBrowserFrameView, TabIconView::TabContentsProvider implementation:
475
476bool OpaqueBrowserFrameView::ShouldTabIconViewAnimate() const {
477  // This function is queried during the creation of the window as the
478  // TabIconView we host is initialized, so we need to NULL check the selected
479  // WebContents because in this condition there is not yet a selected tab.
480  WebContents* current_tab = browser_view()->GetActiveWebContents();
481  return current_tab ? current_tab->IsLoading() : false;
482}
483
484gfx::ImageSkia OpaqueBrowserFrameView::GetFaviconForTabIconView() {
485  views::WidgetDelegate* delegate = frame()->widget_delegate();
486  if (!delegate) {
487    LOG(WARNING) << "delegate is NULL, returning safe default.";
488    return gfx::ImageSkia();
489  }
490  return delegate->GetWindowIcon();
491}
492
493///////////////////////////////////////////////////////////////////////////////
494// OpaqueBrowserFrameView, protected:
495
496void OpaqueBrowserFrameView::Observe(
497    int type,
498    const content::NotificationSource& source,
499    const content::NotificationDetails& details) {
500  switch (type) {
501    case chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED:
502      UpdateAvatarInfo();
503      break;
504    default:
505      NOTREACHED() << "Got a notification we didn't register for!";
506      break;
507  }
508}
509
510///////////////////////////////////////////////////////////////////////////////
511// OpaqueBrowserFrameView, private:
512
513views::ImageButton* OpaqueBrowserFrameView::InitWindowCaptionButton(
514    int normal_image_id,
515    int hot_image_id,
516    int pushed_image_id,
517    int mask_image_id,
518    int accessibility_string_id) {
519  views::ImageButton* button = new views::ImageButton(this);
520  ui::ThemeProvider* tp = frame()->GetThemeProvider();
521  button->SetImage(views::CustomButton::STATE_NORMAL,
522                   tp->GetImageSkiaNamed(normal_image_id));
523  button->SetImage(views::CustomButton::STATE_HOVERED,
524                   tp->GetImageSkiaNamed(hot_image_id));
525  button->SetImage(views::CustomButton::STATE_PRESSED,
526                   tp->GetImageSkiaNamed(pushed_image_id));
527  if (browser_view()->IsBrowserTypeNormal()) {
528    button->SetBackground(
529        tp->GetColor(ThemeProperties::COLOR_BUTTON_BACKGROUND),
530        tp->GetImageSkiaNamed(IDR_THEME_WINDOW_CONTROL_BACKGROUND),
531        tp->GetImageSkiaNamed(mask_image_id));
532  }
533  button->SetAccessibleName(
534      l10n_util::GetStringUTF16(accessibility_string_id));
535  AddChildView(button);
536  return button;
537}
538
539int OpaqueBrowserFrameView::FrameBorderThickness(bool restored) const {
540  return (!restored && (frame()->IsMaximized() || frame()->IsFullscreen())) ?
541      0 : kFrameBorderThickness;
542}
543
544int OpaqueBrowserFrameView::TopResizeHeight() const {
545  return FrameBorderThickness(false) - kTopResizeAdjust;
546}
547
548int OpaqueBrowserFrameView::NonClientBorderThickness() const {
549  // When we fill the screen, we don't show a client edge.
550  return FrameBorderThickness(false) +
551      ((frame()->IsMaximized() || frame()->IsFullscreen()) ?
552       0 : kClientEdgeThickness);
553}
554
555int OpaqueBrowserFrameView::CaptionButtonY(bool restored) const {
556  // Maximized buttons start at window top so that even if their images aren't
557  // drawn flush with the screen edge, they still obey Fitts' Law.
558  return (!restored && frame()->IsMaximized()) ?
559      FrameBorderThickness(false) : kFrameShadowThickness;
560}
561
562int OpaqueBrowserFrameView::TitlebarBottomThickness(bool restored) const {
563  return kTitlebarTopAndBottomEdgeThickness +
564      ((!restored && frame()->IsMaximized()) ? 0 : kClientEdgeThickness);
565}
566
567int OpaqueBrowserFrameView::IconSize() const {
568#if defined(OS_WIN)
569  // This metric scales up if either the titlebar height or the titlebar font
570  // size are increased.
571  return GetSystemMetrics(SM_CYSMICON);
572#else
573  return std::max(BrowserFrame::GetTitleFont().GetHeight(), kIconMinimumSize);
574#endif
575}
576
577gfx::Rect OpaqueBrowserFrameView::IconBounds() const {
578  int size = IconSize();
579  int frame_thickness = FrameBorderThickness(false);
580  int y;
581  views::WidgetDelegate* delegate = frame()->widget_delegate();
582  if (delegate && (delegate->ShouldShowWindowIcon() ||
583                   delegate->ShouldShowWindowTitle())) {
584    // Our frame border has a different "3D look" than Windows'.  Theirs has a
585    // more complex gradient on the top that they push their icon/title below;
586    // then the maximized window cuts this off and the icon/title are centered
587    // in the remaining space.  Because the apparent shape of our border is
588    // simpler, using the same positioning makes things look slightly uncentered
589    // with restored windows, so when the window is restored, instead of
590    // calculating the remaining space from below the frame border, we calculate
591    // from below the 3D edge.
592    int unavailable_px_at_top = frame()->IsMaximized() ?
593        frame_thickness : kTitlebarTopAndBottomEdgeThickness;
594    // When the icon is shorter than the minimum space we reserve for the
595    // caption button, we vertically center it.  We want to bias rounding to put
596    // extra space above the icon, since the 3D edge (+ client edge, for
597    // restored windows) below looks (to the eye) more like additional space
598    // than does the 3D edge (or nothing at all, for maximized windows) above;
599    // hence the +1.
600    y = unavailable_px_at_top + (NonClientTopBorderHeight(false) -
601        unavailable_px_at_top - size - TitlebarBottomThickness(false) + 1) / 2;
602  } else {
603    // For "browser mode" windows, we use the native positioning, which is just
604    // below the top frame border.
605    y = frame_thickness;
606  }
607  return gfx::Rect(frame_thickness + kIconLeftSpacing, y, size, size);
608}
609
610gfx::Rect OpaqueBrowserFrameView::GetBoundsForTabStripAndAvatarArea(
611    views::View* tabstrip) const {
612  int available_width = width();
613  if (minimize_button_) {
614    available_width = minimize_button_->x();
615  } else if (browser_view()->window_switcher_button()) {
616    // We don't have the sysmenu buttons in Windows 8 metro mode. However there
617    // are buttons like the window switcher which are drawn in the non client
618    // are in the BrowserView. We need to ensure that the tab strip does not
619    // draw on the window switcher button.
620    available_width -= browser_view()->window_switcher_button()->width();
621  }
622  const int caption_spacing = frame()->IsMaximized() ?
623      kNewTabCaptionMaximizedSpacing : kNewTabCaptionRestoredSpacing;
624  const int tabstrip_x = NonClientBorderThickness();
625  const int tabstrip_width = available_width - tabstrip_x - caption_spacing;
626  return gfx::Rect(tabstrip_x, GetTabStripInsets(false).top,
627                   std::max(0, tabstrip_width),
628                   tabstrip->GetPreferredSize().height());
629}
630
631void OpaqueBrowserFrameView::PaintRestoredFrameBorder(gfx::Canvas* canvas) {
632  frame_background_->set_frame_color(GetFrameColor());
633  frame_background_->set_theme_image(GetFrameImage());
634  frame_background_->set_theme_overlay_image(GetFrameOverlayImage());
635  frame_background_->set_top_area_height(GetTopAreaHeight());
636
637  ui::ThemeProvider* tp = GetThemeProvider();
638  frame_background_->SetSideImages(
639      tp->GetImageSkiaNamed(IDR_WINDOW_LEFT_SIDE),
640      tp->GetImageSkiaNamed(IDR_WINDOW_TOP_CENTER),
641      tp->GetImageSkiaNamed(IDR_WINDOW_RIGHT_SIDE),
642      tp->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_CENTER));
643  frame_background_->SetCornerImages(
644      tp->GetImageSkiaNamed(IDR_WINDOW_TOP_LEFT_CORNER),
645      tp->GetImageSkiaNamed(IDR_WINDOW_TOP_RIGHT_CORNER),
646      tp->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_LEFT_CORNER),
647      tp->GetImageSkiaNamed(IDR_WINDOW_BOTTOM_RIGHT_CORNER));
648  frame_background_->PaintRestored(canvas, this);
649
650  // Note: When we don't have a toolbar, we need to draw some kind of bottom
651  // edge here.  Because the App Window graphics we use for this have an
652  // attached client edge and their sizing algorithm is a little involved, we do
653  // all this in PaintRestoredClientEdge().
654}
655
656void OpaqueBrowserFrameView::PaintMaximizedFrameBorder(gfx::Canvas* canvas) {
657  frame_background_->set_frame_color(GetFrameColor());
658  frame_background_->set_theme_image(GetFrameImage());
659  frame_background_->set_theme_overlay_image(GetFrameOverlayImage());
660  frame_background_->set_top_area_height(GetTopAreaHeight());
661
662  // Theme frame must be aligned with the tabstrip as if we were
663  // in restored mode.  Note that the top of the tabstrip is
664  // kTabstripTopShadowThickness px off the top of the screen.
665  int theme_background_y = -(GetTabStripInsets(true).top +
666      kTabstripTopShadowThickness);
667  frame_background_->set_theme_background_y(theme_background_y);
668
669  frame_background_->PaintMaximized(canvas, this);
670
671  // TODO(jamescook): Migrate this into FrameBackground.
672  if (!browser_view()->IsToolbarVisible()) {
673    // There's no toolbar to edge the frame border, so we need to draw a bottom
674    // edge.  The graphic we use for this has a built in client edge, so we clip
675    // it off the bottom.
676    gfx::ImageSkia* top_center =
677        GetThemeProvider()->GetImageSkiaNamed(IDR_APP_TOP_CENTER);
678    int edge_height = top_center->height() - kClientEdgeThickness;
679    canvas->TileImageInt(*top_center, 0,
680        frame()->client_view()->y() - edge_height, width(), edge_height);
681  }
682}
683
684void OpaqueBrowserFrameView::PaintToolbarBackground(gfx::Canvas* canvas) {
685  gfx::Rect toolbar_bounds(browser_view()->GetToolbarBounds());
686  if (toolbar_bounds.IsEmpty())
687    return;
688  gfx::Point toolbar_origin(toolbar_bounds.origin());
689  ConvertPointToTarget(browser_view(), this, &toolbar_origin);
690  toolbar_bounds.set_origin(toolbar_origin);
691
692  int x = toolbar_bounds.x();
693  int w = toolbar_bounds.width();
694  int y = toolbar_bounds.y();
695  int h = toolbar_bounds.height();
696
697  // Gross hack: We split the toolbar images into two pieces, since sometimes
698  // (popup mode) the toolbar isn't tall enough to show the whole image.  The
699  // split happens between the top shadow section and the bottom gradient
700  // section so that we never break the gradient.
701  int split_point = kFrameShadowThickness * 2;
702  int bottom_y = y + split_point;
703  ui::ThemeProvider* tp = GetThemeProvider();
704  gfx::ImageSkia* toolbar_left = tp->GetImageSkiaNamed(
705      IDR_CONTENT_TOP_LEFT_CORNER);
706  int bottom_edge_height = std::min(toolbar_left->height(), h) - split_point;
707
708  // Split our canvas out so we can mask out the corners of the toolbar
709  // without masking out the frame.
710  canvas->SaveLayerAlpha(
711      255, gfx::Rect(x - kClientEdgeThickness, y, w + kClientEdgeThickness * 3,
712                     h));
713
714  // Paint the bottom rect.
715  canvas->FillRect(gfx::Rect(x, bottom_y, w, bottom_edge_height),
716                   tp->GetColor(ThemeProperties::COLOR_TOOLBAR));
717
718  // Tile the toolbar image starting at the frame edge on the left and where the
719  // horizontal tabstrip is (or would be) on the top.
720  gfx::ImageSkia* theme_toolbar = tp->GetImageSkiaNamed(IDR_THEME_TOOLBAR);
721  canvas->TileImageInt(*theme_toolbar,
722                       x + GetThemeBackgroundXInset(),
723                       bottom_y - GetTabStripInsets(false).top,
724                       x, bottom_y, w, theme_toolbar->height());
725
726  // Draw rounded corners for the tab.
727  gfx::ImageSkia* toolbar_left_mask =
728      tp->GetImageSkiaNamed(IDR_CONTENT_TOP_LEFT_CORNER_MASK);
729  gfx::ImageSkia* toolbar_right_mask =
730      tp->GetImageSkiaNamed(IDR_CONTENT_TOP_RIGHT_CORNER_MASK);
731
732  // We mask out the corners by using the DestinationIn transfer mode,
733  // which keeps the RGB pixels from the destination and the alpha from
734  // the source.
735  SkPaint paint;
736  paint.setXfermodeMode(SkXfermode::kDstIn_Mode);
737
738  // Mask the left edge.
739  int left_x = x - kContentEdgeShadowThickness;
740  canvas->DrawImageInt(*toolbar_left_mask, 0, 0, toolbar_left_mask->width(),
741                       split_point, left_x, y, toolbar_left_mask->width(),
742                       split_point, false, paint);
743  canvas->DrawImageInt(*toolbar_left_mask, 0,
744      toolbar_left_mask->height() - bottom_edge_height,
745      toolbar_left_mask->width(), bottom_edge_height, left_x, bottom_y,
746      toolbar_left_mask->width(), bottom_edge_height, false, paint);
747
748  // Mask the right edge.
749  int right_x =
750      x + w - toolbar_right_mask->width() + kContentEdgeShadowThickness;
751  canvas->DrawImageInt(*toolbar_right_mask, 0, 0, toolbar_right_mask->width(),
752                       split_point, right_x, y, toolbar_right_mask->width(),
753                       split_point, false, paint);
754  canvas->DrawImageInt(*toolbar_right_mask, 0,
755      toolbar_right_mask->height() - bottom_edge_height,
756      toolbar_right_mask->width(), bottom_edge_height, right_x, bottom_y,
757      toolbar_right_mask->width(), bottom_edge_height, false, paint);
758  canvas->Restore();
759
760  canvas->DrawImageInt(*toolbar_left, 0, 0, toolbar_left->width(), split_point,
761                       left_x, y, toolbar_left->width(), split_point, false);
762  canvas->DrawImageInt(*toolbar_left, 0,
763      toolbar_left->height() - bottom_edge_height, toolbar_left->width(),
764      bottom_edge_height, left_x, bottom_y, toolbar_left->width(),
765      bottom_edge_height, false);
766
767  gfx::ImageSkia* toolbar_center =
768      tp->GetImageSkiaNamed(IDR_CONTENT_TOP_CENTER);
769  canvas->TileImageInt(*toolbar_center, 0, 0, left_x + toolbar_left->width(),
770      y, right_x - (left_x + toolbar_left->width()),
771      split_point);
772
773  gfx::ImageSkia* toolbar_right = tp->GetImageSkiaNamed(
774      IDR_CONTENT_TOP_RIGHT_CORNER);
775  canvas->DrawImageInt(*toolbar_right, 0, 0, toolbar_right->width(),
776      split_point, right_x, y, toolbar_right->width(), split_point, false);
777  canvas->DrawImageInt(*toolbar_right, 0,
778      toolbar_right->height() - bottom_edge_height, toolbar_right->width(),
779      bottom_edge_height, right_x, bottom_y, toolbar_right->width(),
780      bottom_edge_height, false);
781
782  // Draw the content/toolbar separator.
783  canvas->FillRect(
784      gfx::Rect(x + kClientEdgeThickness,
785                toolbar_bounds.bottom() - kClientEdgeThickness,
786                w - (2 * kClientEdgeThickness),
787                kClientEdgeThickness),
788      ThemeProperties::GetDefaultColor(
789          ThemeProperties::COLOR_TOOLBAR_SEPARATOR));
790}
791
792void OpaqueBrowserFrameView::PaintRestoredClientEdge(gfx::Canvas* canvas) {
793  ui::ThemeProvider* tp = GetThemeProvider();
794  int client_area_top = frame()->client_view()->y();
795  int image_top = client_area_top;
796
797  gfx::Rect client_area_bounds = CalculateClientAreaBounds(width(), height());
798  SkColor toolbar_color = tp->GetColor(ThemeProperties::COLOR_TOOLBAR);
799
800  if (browser_view()->IsToolbarVisible()) {
801    // The client edge images always start below the toolbar corner images.  The
802    // client edge filled rects start there or at the bottom of the toolbar,
803    // whichever is shorter.
804    gfx::Rect toolbar_bounds(browser_view()->GetToolbarBounds());
805    image_top += toolbar_bounds.y() +
806        tp->GetImageSkiaNamed(IDR_CONTENT_TOP_LEFT_CORNER)->height();
807    client_area_top = std::min(image_top,
808        client_area_top + toolbar_bounds.bottom() - kClientEdgeThickness);
809  } else if (!browser_view()->IsTabStripVisible()) {
810    // The toolbar isn't going to draw a client edge for us, so draw one
811    // ourselves.
812    gfx::ImageSkia* top_left = tp->GetImageSkiaNamed(IDR_APP_TOP_LEFT);
813    gfx::ImageSkia* top_center = tp->GetImageSkiaNamed(IDR_APP_TOP_CENTER);
814    gfx::ImageSkia* top_right = tp->GetImageSkiaNamed(IDR_APP_TOP_RIGHT);
815    int top_edge_y = client_area_top - top_center->height();
816    int height = client_area_top - top_edge_y;
817
818    canvas->DrawImageInt(*top_left, 0, 0, top_left->width(), height,
819        client_area_bounds.x() - top_left->width(), top_edge_y,
820        top_left->width(), height, false);
821    canvas->TileImageInt(*top_center, 0, 0, client_area_bounds.x(), top_edge_y,
822      client_area_bounds.width(), std::min(height, top_center->height()));
823    canvas->DrawImageInt(*top_right, 0, 0, top_right->width(), height,
824        client_area_bounds.right(), top_edge_y,
825        top_right->width(), height, false);
826
827    // Draw the toolbar color across the top edge.
828    canvas->FillRect(gfx::Rect(client_area_bounds.x() - kClientEdgeThickness,
829        client_area_top - kClientEdgeThickness,
830        client_area_bounds.width() + (2 * kClientEdgeThickness),
831        kClientEdgeThickness), toolbar_color);
832  }
833
834  int client_area_bottom =
835      std::max(client_area_top, height() - NonClientBorderThickness());
836  int image_height = client_area_bottom - image_top;
837
838  // Draw the client edge images.
839  // Draw the client edge images.
840  gfx::ImageSkia* right = tp->GetImageSkiaNamed(IDR_CONTENT_RIGHT_SIDE);
841  canvas->TileImageInt(*right, client_area_bounds.right(), image_top,
842                       right->width(), image_height);
843  canvas->DrawImageInt(
844      *tp->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_RIGHT_CORNER),
845      client_area_bounds.right(), client_area_bottom);
846  gfx::ImageSkia* bottom = tp->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_CENTER);
847  canvas->TileImageInt(*bottom, client_area_bounds.x(),
848      client_area_bottom, client_area_bounds.width(),
849      bottom->height());
850  gfx::ImageSkia* bottom_left =
851      tp->GetImageSkiaNamed(IDR_CONTENT_BOTTOM_LEFT_CORNER);
852  canvas->DrawImageInt(*bottom_left,
853      client_area_bounds.x() - bottom_left->width(), client_area_bottom);
854  gfx::ImageSkia* left = tp->GetImageSkiaNamed(IDR_CONTENT_LEFT_SIDE);
855  canvas->TileImageInt(*left, client_area_bounds.x() - left->width(),
856                       image_top, left->width(), image_height);
857
858  // Draw the toolbar color so that the client edges show the right color even
859  // where not covered by the toolbar image.  NOTE: We do this after drawing the
860  // images because the images are meant to alpha-blend atop the frame whereas
861  // these rects are meant to be fully opaque, without anything overlaid.
862  canvas->FillRect(gfx::Rect(client_area_bounds.x() - kClientEdgeThickness,
863      client_area_top, kClientEdgeThickness,
864      client_area_bottom + kClientEdgeThickness - client_area_top),
865       toolbar_color);
866  canvas->FillRect(gfx::Rect(client_area_bounds.x(), client_area_bottom,
867                             client_area_bounds.width(), kClientEdgeThickness),
868                   toolbar_color);
869  canvas->FillRect(gfx::Rect(client_area_bounds.right(), client_area_top,
870      kClientEdgeThickness,
871      client_area_bottom + kClientEdgeThickness - client_area_top),
872      toolbar_color);
873}
874
875SkColor OpaqueBrowserFrameView::GetFrameColor() const {
876  bool is_incognito = browser_view()->IsOffTheRecord();
877  if (browser_view()->IsBrowserTypeNormal()) {
878    if (ShouldPaintAsActive()) {
879      return GetThemeProvider()->GetColor(is_incognito ?
880          ThemeProperties::COLOR_FRAME_INCOGNITO :
881          ThemeProperties::COLOR_FRAME);
882    }
883    return GetThemeProvider()->GetColor(is_incognito ?
884        ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE :
885        ThemeProperties::COLOR_FRAME_INACTIVE);
886  }
887  // Never theme app and popup windows.
888  if (ShouldPaintAsActive()) {
889    return ThemeProperties::GetDefaultColor(is_incognito ?
890        ThemeProperties::COLOR_FRAME_INCOGNITO : ThemeProperties::COLOR_FRAME);
891  }
892  return ThemeProperties::GetDefaultColor(is_incognito ?
893      ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE :
894      ThemeProperties::COLOR_FRAME_INACTIVE);
895}
896
897gfx::ImageSkia* OpaqueBrowserFrameView::GetFrameImage() const {
898  bool is_incognito = browser_view()->IsOffTheRecord();
899  int resource_id;
900  if (browser_view()->IsBrowserTypeNormal()) {
901    if (ShouldPaintAsActive()) {
902      resource_id = is_incognito ?
903          IDR_THEME_FRAME_INCOGNITO : IDR_THEME_FRAME;
904    } else {
905      resource_id = is_incognito ?
906          IDR_THEME_FRAME_INCOGNITO_INACTIVE : IDR_THEME_FRAME_INACTIVE;
907    }
908    return GetThemeProvider()->GetImageSkiaNamed(resource_id);
909  }
910  // Never theme app and popup windows.
911  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
912  if (ShouldPaintAsActive()) {
913    resource_id = is_incognito ?
914        IDR_THEME_FRAME_INCOGNITO : IDR_FRAME;
915  } else {
916    resource_id = is_incognito ?
917        IDR_THEME_FRAME_INCOGNITO_INACTIVE : IDR_THEME_FRAME_INACTIVE;
918  }
919  return rb.GetImageSkiaNamed(resource_id);
920}
921
922gfx::ImageSkia* OpaqueBrowserFrameView::GetFrameOverlayImage() const {
923  ui::ThemeProvider* tp = GetThemeProvider();
924  if (tp->HasCustomImage(IDR_THEME_FRAME_OVERLAY) &&
925      browser_view()->IsBrowserTypeNormal() &&
926      !browser_view()->IsOffTheRecord()) {
927    return tp->GetImageSkiaNamed(ShouldPaintAsActive() ?
928        IDR_THEME_FRAME_OVERLAY : IDR_THEME_FRAME_OVERLAY_INACTIVE);
929  }
930  return NULL;
931}
932
933int OpaqueBrowserFrameView::GetTopAreaHeight() const {
934  gfx::ImageSkia* frame_image = GetFrameImage();
935  int top_area_height = frame_image->height();
936  if (browser_view()->IsTabStripVisible()) {
937    top_area_height = std::max(top_area_height,
938      GetBoundsForTabStrip(browser_view()->tabstrip()).bottom());
939  }
940  return top_area_height;
941}
942
943void OpaqueBrowserFrameView::LayoutWindowControls() {
944  if (!ShouldAddDefaultCaptionButtons())
945    return;
946  bool is_maximized = frame()->IsMaximized();
947  close_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT,
948                                   views::ImageButton::ALIGN_BOTTOM);
949  int caption_y = CaptionButtonY(false);
950  // There should always be the same number of non-shadow pixels visible to the
951  // side of the caption buttons.  In maximized mode we extend the rightmost
952  // button to the screen corner to obey Fitts' Law.
953  int right_extra_width = is_maximized ?
954      (kFrameBorderThickness - kFrameShadowThickness) : 0;
955  gfx::Size close_button_size = close_button_->GetPreferredSize();
956  close_button_->SetBounds(width() - FrameBorderThickness(false) -
957      right_extra_width - close_button_size.width(), caption_y,
958      close_button_size.width() + right_extra_width,
959      close_button_size.height());
960
961  // When the window is restored, we show a maximized button; otherwise, we show
962  // a restore button.
963  bool is_restored = !is_maximized && !frame()->IsMinimized();
964  views::ImageButton* invisible_button = is_restored ?
965      restore_button_ : maximize_button_;
966  invisible_button->SetVisible(false);
967
968  views::ImageButton* visible_button = is_restored ?
969      maximize_button_ : restore_button_;
970  visible_button->SetVisible(true);
971  visible_button->SetImageAlignment(views::ImageButton::ALIGN_LEFT,
972                                    views::ImageButton::ALIGN_BOTTOM);
973  gfx::Size visible_button_size = visible_button->GetPreferredSize();
974  visible_button->SetBounds(close_button_->x() - visible_button_size.width(),
975                            caption_y, visible_button_size.width(),
976                            visible_button_size.height());
977
978  minimize_button_->SetVisible(true);
979  minimize_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT,
980                                      views::ImageButton::ALIGN_BOTTOM);
981  gfx::Size minimize_button_size = minimize_button_->GetPreferredSize();
982  minimize_button_->SetBounds(
983      visible_button->x() - minimize_button_size.width(), caption_y,
984      minimize_button_size.width(),
985      minimize_button_size.height());
986}
987
988void OpaqueBrowserFrameView::LayoutTitleBar() {
989  const views::WidgetDelegate* delegate = frame()->widget_delegate();
990  if (delegate) {
991    gfx::Rect icon_bounds(IconBounds());
992    if (delegate->ShouldShowWindowIcon())
993      window_icon_->SetBoundsRect(icon_bounds);
994
995    window_title_->SetVisible(delegate->ShouldShowWindowTitle());
996    if (delegate->ShouldShowWindowTitle()) {
997      window_title_->SetText(delegate->GetWindowTitle());
998      const int title_x = delegate->ShouldShowWindowIcon() ?
999          icon_bounds.right() + kIconTitleSpacing : icon_bounds.x();
1000      window_title_->SetBounds(title_x, icon_bounds.y(),
1001          std::max(0, minimize_button_->x() - kTitleLogoSpacing - title_x),
1002          icon_bounds.height());
1003    }
1004  }
1005}
1006
1007void OpaqueBrowserFrameView::LayoutAvatar() {
1008  // Even though the avatar is used for both incognito and profiles we always
1009  // use the incognito icon to layout the avatar button. The profile icon
1010  // can be customized so we can't depend on its size to perform layout.
1011  gfx::ImageSkia incognito_icon = browser_view()->GetOTRAvatarIcon();
1012
1013  int avatar_bottom = GetTabStripInsets(false).top +
1014      browser_view()->GetTabStripHeight() - kAvatarBottomSpacing;
1015  int avatar_restored_y = avatar_bottom - incognito_icon.height();
1016  int avatar_y = frame()->IsMaximized() ?
1017      (NonClientTopBorderHeight(false) + kTabstripTopShadowThickness) :
1018      avatar_restored_y;
1019  avatar_bounds_.SetRect(NonClientBorderThickness() + kAvatarLeftSpacing,
1020      avatar_y, incognito_icon.width(),
1021      browser_view()->ShouldShowAvatar() ? (avatar_bottom - avatar_y) : 0);
1022
1023  if (avatar_button())
1024    avatar_button()->SetBoundsRect(avatar_bounds_);
1025}
1026
1027gfx::Rect OpaqueBrowserFrameView::CalculateClientAreaBounds(int width,
1028                                                            int height) const {
1029  int top_height = NonClientTopBorderHeight(false);
1030  int border_thickness = NonClientBorderThickness();
1031  return gfx::Rect(border_thickness, top_height,
1032                   std::max(0, width - (2 * border_thickness)),
1033                   std::max(0, height - GetReservedHeight() -
1034                       top_height - border_thickness));
1035}
1036