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