Home | History | Annotate | Download | only in frame
      1 // Copyright (c) 2012 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/app_panel_browser_frame_view.h"
      6 
      7 #include "base/compiler_specific.h"
      8 #include "base/strings/utf_string_conversions.h"
      9 #include "chrome/browser/ui/views/frame/browser_frame.h"
     10 #include "chrome/browser/ui/views/frame/browser_view.h"
     11 #include "chrome/browser/ui/views/tab_icon_view.h"
     12 #include "content/public/browser/web_contents.h"
     13 #include "grit/chromium_strings.h"
     14 #include "grit/generated_resources.h"
     15 #include "grit/theme_resources.h"
     16 #include "grit/ui_resources.h"
     17 #include "ui/base/hit_test.h"
     18 #include "ui/base/l10n/l10n_util.h"
     19 #include "ui/base/resource/resource_bundle.h"
     20 #include "ui/gfx/canvas.h"
     21 #include "ui/gfx/font.h"
     22 #include "ui/gfx/path.h"
     23 #include "ui/views/color_constants.h"
     24 #include "ui/views/controls/button/image_button.h"
     25 #include "ui/views/widget/widget.h"
     26 #include "ui/views/widget/widget_delegate.h"
     27 
     28 using content::WebContents;
     29 
     30 namespace {
     31 
     32 // The frame border is only visible in restored mode and is hardcoded to 1 px on
     33 // each side regardless of the system window border size.
     34 const int kFrameBorderThickness = 1;
     35 // In the window corners, the resize areas don't actually expand bigger, but the
     36 // 16 px at the end of each edge triggers diagonal resizing.
     37 const int kResizeAreaCornerSize = 16;
     38 // The titlebar never shrinks too short to show the caption button plus some
     39 // padding below it.
     40 const int kCaptionButtonHeightWithPadding = 27;
     41 // The titlebar has a 2 px 3D edge along the bottom, and we reserve 2 px (1 for
     42 // border, 1 for padding) along the top.
     43 const int kTitlebarTopAndBottomEdgeThickness = 2;
     44 // The icon is inset 6 px from the left frame border.
     45 const int kIconLeftSpacing = 6;
     46 // The icon never shrinks below 16 px on a side.
     47 const int kIconMinimumSize = 16;
     48 // There is a 4 px gap between the icon and the title text.
     49 const int kIconTitleSpacing = 4;
     50 // There is a 5 px gap between the title text and the close button.
     51 const int kTitleCloseButtonSpacing = 5;
     52 // There is a 4 px gap between the close button and the frame border.
     53 const int kCloseButtonFrameBorderSpacing = 4;
     54 
     55 const SkColor kFrameColorAppPanel = SK_ColorWHITE;
     56 const SkColor kFrameColorAppPanelInactive = SK_ColorWHITE;
     57 
     58 }  // namespace
     59 
     60 ///////////////////////////////////////////////////////////////////////////////
     61 // AppPanelBrowserFrameView, public:
     62 
     63 AppPanelBrowserFrameView::AppPanelBrowserFrameView(BrowserFrame* frame,
     64                                                    BrowserView* browser_view)
     65     : BrowserNonClientFrameView(frame, browser_view),
     66       close_button_(new views::ImageButton(this)),
     67       window_icon_(NULL) {
     68   DCHECK(browser_view->ShouldShowWindowIcon());
     69   DCHECK(browser_view->ShouldShowWindowTitle());
     70 
     71   frame->set_frame_type(views::Widget::FRAME_TYPE_FORCE_CUSTOM);
     72 
     73   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
     74   close_button_->SetImage(views::CustomButton::STATE_NORMAL,
     75                           rb.GetImageSkiaNamed(IDR_CLOSE_2));
     76   close_button_->SetImage(views::CustomButton::STATE_HOVERED,
     77                           rb.GetImageSkiaNamed(IDR_CLOSE_2_H));
     78   close_button_->SetImage(views::CustomButton::STATE_PRESSED,
     79                           rb.GetImageSkiaNamed(IDR_CLOSE_2_P));
     80   close_button_->SetAccessibleName(
     81       l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE));
     82   AddChildView(close_button_);
     83 
     84   window_icon_ = new TabIconView(this);
     85   window_icon_->set_is_light(true);
     86   AddChildView(window_icon_);
     87   window_icon_->Update();
     88 }
     89 
     90 AppPanelBrowserFrameView::~AppPanelBrowserFrameView() {
     91 }
     92 
     93 ///////////////////////////////////////////////////////////////////////////////
     94 // AppPanelBrowserFrameView, BrowserNonClientFrameView implementation:
     95 
     96 gfx::Rect AppPanelBrowserFrameView::GetBoundsForTabStrip(
     97     views::View* tabstrip) const {
     98   // App panels never show a tab strip.
     99   NOTREACHED();
    100   return gfx::Rect();
    101 }
    102 
    103 BrowserNonClientFrameView::TabStripInsets
    104 AppPanelBrowserFrameView::GetTabStripInsets(bool restored) const {
    105   // App panels are not themed and don't need this.
    106   return TabStripInsets();
    107 }
    108 
    109 int AppPanelBrowserFrameView::GetThemeBackgroundXInset() const {
    110   return 0;
    111 }
    112 
    113 void AppPanelBrowserFrameView::UpdateThrobber(bool running) {
    114   window_icon_->Update();
    115 }
    116 
    117 gfx::Size AppPanelBrowserFrameView::GetMinimumSize() {
    118   gfx::Size min_size(browser_view()->GetMinimumSize());
    119   int border_thickness = NonClientBorderThickness();
    120   min_size.Enlarge(2 * border_thickness,
    121                    NonClientTopBorderHeight() + border_thickness);
    122 
    123   min_size.set_width(std::max(min_size.width(),
    124       (2 * FrameBorderThickness()) + kIconLeftSpacing + IconSize() +
    125       kTitleCloseButtonSpacing + kCloseButtonFrameBorderSpacing));
    126   return min_size;
    127 }
    128 
    129 ///////////////////////////////////////////////////////////////////////////////
    130 // AppPanelBrowserFrameView, views::NonClientFrameView implementation:
    131 
    132 gfx::Rect AppPanelBrowserFrameView::GetBoundsForClientView() const {
    133   return client_view_bounds_;
    134 }
    135 
    136 gfx::Rect AppPanelBrowserFrameView::GetWindowBoundsForClientBounds(
    137     const gfx::Rect& client_bounds) const {
    138   int top_height = NonClientTopBorderHeight();
    139   int border_thickness = NonClientBorderThickness();
    140   return gfx::Rect(std::max(0, client_bounds.x() - border_thickness),
    141                    std::max(0, client_bounds.y() - top_height),
    142                    client_bounds.width() + (2 * border_thickness),
    143                    client_bounds.height() + top_height + border_thickness);
    144 }
    145 
    146 int AppPanelBrowserFrameView::NonClientHitTest(const gfx::Point& point) {
    147   if (!bounds().Contains(point))
    148     return HTNOWHERE;
    149 
    150   int frame_component = frame()->client_view()->NonClientHitTest(point);
    151 
    152   // See if we're in the sysmenu region.  (We check the ClientView first to be
    153   // consistent with OpaqueBrowserFrameView; it's not really necessary here.)
    154   gfx::Rect sysmenu_rect(IconBounds());
    155   // In maximized mode we extend the rect to the screen corner to take advantage
    156   // of Fitts' Law.
    157   if (frame()->IsMaximized())
    158     sysmenu_rect.SetRect(0, 0, sysmenu_rect.right(), sysmenu_rect.bottom());
    159   sysmenu_rect.set_x(GetMirroredXForRect(sysmenu_rect));
    160   if (sysmenu_rect.Contains(point))
    161     return (frame_component == HTCLIENT) ? HTCLIENT : HTSYSMENU;
    162 
    163   if (frame_component != HTNOWHERE)
    164     return frame_component;
    165 
    166   // Then see if the point is within any of the window controls.
    167   if (close_button_->visible() &&
    168       close_button_->GetMirroredBounds().Contains(point))
    169     return HTCLOSE;
    170 
    171   int window_component = GetHTComponentForFrame(point,
    172       NonClientBorderThickness(), NonClientBorderThickness(),
    173       kResizeAreaCornerSize, kResizeAreaCornerSize,
    174       frame()->widget_delegate()->CanResize());
    175   // Fall back to the caption if no other component matches.
    176   return (window_component == HTNOWHERE) ? HTCAPTION : window_component;
    177 }
    178 
    179 void AppPanelBrowserFrameView::GetWindowMask(const gfx::Size& size,
    180                                              gfx::Path* window_mask) {
    181   DCHECK(window_mask);
    182 
    183   if (frame()->IsMaximized())
    184     return;
    185 
    186   // Redefine the window visible region for the new size.
    187   window_mask->moveTo(0, 3);
    188   window_mask->lineTo(1, 2);
    189   window_mask->lineTo(1, 1);
    190   window_mask->lineTo(2, 1);
    191   window_mask->lineTo(3, 0);
    192 
    193   window_mask->lineTo(SkIntToScalar(size.width() - 3), 0);
    194   window_mask->lineTo(SkIntToScalar(size.width() - 2), 1);
    195   window_mask->lineTo(SkIntToScalar(size.width() - 1), 1);
    196   window_mask->lineTo(SkIntToScalar(size.width() - 1), 2);
    197   window_mask->lineTo(SkIntToScalar(size.width()), 3);
    198 
    199   window_mask->lineTo(SkIntToScalar(size.width()),
    200                       SkIntToScalar(size.height()));
    201   window_mask->lineTo(0, SkIntToScalar(size.height()));
    202   window_mask->close();
    203 }
    204 
    205 void AppPanelBrowserFrameView::ResetWindowControls() {
    206   // The close button isn't affected by this constraint.
    207 }
    208 
    209 void AppPanelBrowserFrameView::UpdateWindowIcon() {
    210   window_icon_->SchedulePaint();
    211 }
    212 
    213 
    214 ///////////////////////////////////////////////////////////////////////////////
    215 // AppPanelBrowserFrameView, views::View overrides:
    216 
    217 void AppPanelBrowserFrameView::OnPaint(gfx::Canvas* canvas) {
    218   if (frame()->IsMaximized())
    219     PaintMaximizedFrameBorder(canvas);
    220   else
    221     PaintRestoredFrameBorder(canvas);
    222   PaintTitleBar(canvas);
    223   if (!frame()->IsMaximized())
    224     PaintRestoredClientEdge(canvas);
    225 }
    226 
    227 void AppPanelBrowserFrameView::Layout() {
    228   LayoutWindowControls();
    229   LayoutTitleBar();
    230   client_view_bounds_ = CalculateClientAreaBounds(width(), height());
    231 }
    232 
    233 ///////////////////////////////////////////////////////////////////////////////
    234 // AppPanelBrowserFrameView, views::ButtonListener implementation:
    235 
    236 void AppPanelBrowserFrameView::ButtonPressed(views::Button* sender,
    237                                              const ui::Event& event) {
    238   if (sender == close_button_)
    239     frame()->Close();
    240 }
    241 
    242 ///////////////////////////////////////////////////////////////////////////////
    243 // AppPanelBrowserFrameView, TabIconView::TabContentsProvider implementation:
    244 
    245 bool AppPanelBrowserFrameView::ShouldTabIconViewAnimate() const {
    246   // This function is queried during the creation of the window as the
    247   // TabIconView we host is initialized, so we need to NULL check the selected
    248   // WebContents because in this condition there is not yet a selected tab.
    249   WebContents* current_tab = browser_view()->GetActiveWebContents();
    250   return current_tab ? current_tab->IsLoading() : false;
    251 }
    252 
    253 gfx::ImageSkia AppPanelBrowserFrameView::GetFaviconForTabIconView() {
    254   return frame()->widget_delegate()->GetWindowIcon();
    255 }
    256 
    257 ///////////////////////////////////////////////////////////////////////////////
    258 // AppPanelBrowserFrameView, private:
    259 
    260 int AppPanelBrowserFrameView::FrameBorderThickness() const {
    261   return frame()->IsMaximized() ? 0 : kFrameBorderThickness;
    262 }
    263 
    264 int AppPanelBrowserFrameView::NonClientBorderThickness() const {
    265   return FrameBorderThickness() +
    266       (frame()->IsMaximized() ? 0 : kClientEdgeThickness);
    267 }
    268 
    269 int AppPanelBrowserFrameView::NonClientTopBorderHeight() const {
    270   return std::max(FrameBorderThickness() + IconSize(),
    271                   FrameBorderThickness() + kCaptionButtonHeightWithPadding) +
    272       TitlebarBottomThickness();
    273 }
    274 
    275 int AppPanelBrowserFrameView::TitlebarBottomThickness() const {
    276   return kTitlebarTopAndBottomEdgeThickness +
    277       (frame()->IsMaximized() ? 0 : kClientEdgeThickness);
    278 }
    279 
    280 int AppPanelBrowserFrameView::IconSize() const {
    281 #if defined(OS_WIN)
    282   // This metric scales up if either the titlebar height or the titlebar font
    283   // size are increased.
    284   return GetSystemMetrics(SM_CYSMICON);
    285 #else
    286   return std::max(BrowserFrame::GetTitleFont().height(), kIconMinimumSize);
    287 #endif
    288 }
    289 
    290 gfx::Rect AppPanelBrowserFrameView::IconBounds() const {
    291   int size = IconSize();
    292   int frame_thickness = FrameBorderThickness();
    293   // Our frame border has a different "3D look" than Windows'.  Theirs has a
    294   // more complex gradient on the top that they push their icon/title below;
    295   // then the maximized window cuts this off and the icon/title are centered
    296   // in the remaining space.  Because the apparent shape of our border is
    297   // simpler, using the same positioning makes things look slightly uncentered
    298   // with restored windows, so when the window is restored, instead of
    299   // calculating the remaining space from below the frame border, we calculate
    300   // from below the top border-plus-padding.
    301   int unavailable_px_at_top = frame()->IsMaximized() ?
    302       frame_thickness : kTitlebarTopAndBottomEdgeThickness;
    303   // When the icon is shorter than the minimum space we reserve for the caption
    304   // button, we vertically center it.  We want to bias rounding to put extra
    305   // space above the icon, since the 3D edge (+ client edge, for restored
    306   // windows) below looks (to the eye) more like additional space than does the
    307   // border + padding (or nothing at all, for maximized windows) above; hence
    308   // the +1.
    309   int y = unavailable_px_at_top + (NonClientTopBorderHeight() -
    310       unavailable_px_at_top - size - TitlebarBottomThickness() + 1) / 2;
    311   return gfx::Rect(frame_thickness + kIconLeftSpacing, y, size, size);
    312 }
    313 
    314 void AppPanelBrowserFrameView::PaintRestoredFrameBorder(gfx::Canvas* canvas) {
    315   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    316 
    317   gfx::ImageSkia* top_left_corner =
    318       rb.GetImageSkiaNamed(IDR_WINDOW_TOP_LEFT_CORNER);
    319   gfx::ImageSkia* top_right_corner =
    320       rb.GetImageSkiaNamed(IDR_WINDOW_TOP_RIGHT_CORNER);
    321   gfx::ImageSkia* top_edge = rb.GetImageSkiaNamed(IDR_WINDOW_TOP_CENTER);
    322   gfx::ImageSkia* right_edge = rb.GetImageSkiaNamed(IDR_WINDOW_RIGHT_SIDE);
    323   gfx::ImageSkia* left_edge = rb.GetImageSkiaNamed(IDR_WINDOW_LEFT_SIDE);
    324   gfx::ImageSkia* bottom_left_corner =
    325       rb.GetImageSkiaNamed(IDR_WINDOW_BOTTOM_LEFT_CORNER);
    326   gfx::ImageSkia* bottom_right_corner =
    327       rb.GetImageSkiaNamed(IDR_WINDOW_BOTTOM_RIGHT_CORNER);
    328   gfx::ImageSkia* bottom_edge = rb.GetImageSkiaNamed(IDR_WINDOW_BOTTOM_CENTER);
    329 
    330   // Window frame mode and color.
    331   gfx::ImageSkia* theme_frame;
    332   SkColor frame_color;
    333   if (ShouldPaintAsActive()) {
    334     theme_frame = rb.GetImageSkiaNamed(IDR_FRAME_APP_PANEL);
    335     frame_color = kFrameColorAppPanel;
    336   } else {
    337     theme_frame = rb.GetImageSkiaNamed(IDR_FRAME_APP_PANEL);
    338     frame_color = kFrameColorAppPanelInactive;
    339   }
    340 
    341   // Fill with the frame color first so we have a constant background for
    342   // areas not covered by the theme image.
    343   canvas->FillRect(gfx::Rect(0, 0, width(), theme_frame->height()),
    344                    frame_color);
    345 
    346   int remaining_height = height() - theme_frame->height();
    347   if (remaining_height > 0) {
    348     // Now fill down the sides.
    349     canvas->FillRect(gfx::Rect(0, theme_frame->height(), left_edge->width(),
    350                                remaining_height), frame_color);
    351     canvas->FillRect(gfx::Rect(width() - right_edge->width(),
    352                                theme_frame->height(), right_edge->width(),
    353                                remaining_height), frame_color);
    354     int center_width = width() - left_edge->width() - right_edge->width();
    355     if (center_width > 0) {
    356       // Now fill the bottom area.
    357       canvas->FillRect(gfx::Rect(left_edge->width(),
    358                                  height() - bottom_edge->height(), center_width,
    359                                  bottom_edge->height()), frame_color);
    360     }
    361   }
    362 
    363   // Draw the theme frame.
    364   canvas->TileImageInt(*theme_frame, 0, 0, width(), theme_frame->height());
    365 
    366   // Top.
    367   canvas->DrawImageInt(*top_left_corner, 0, 0);
    368   canvas->TileImageInt(*top_edge, top_left_corner->width(), 0,
    369                        width() - top_right_corner->width(), top_edge->height());
    370   canvas->DrawImageInt(*top_right_corner,
    371                        width() - top_right_corner->width(), 0);
    372 
    373   // Right.
    374   canvas->TileImageInt(*right_edge, width() - right_edge->width(),
    375       top_right_corner->height(), right_edge->width(),
    376       height() - top_right_corner->height() - bottom_right_corner->height());
    377 
    378   // Bottom.
    379   canvas->DrawImageInt(*bottom_right_corner,
    380                        width() - bottom_right_corner->width(),
    381                        height() - bottom_right_corner->height());
    382   canvas->TileImageInt(*bottom_edge, bottom_left_corner->width(),
    383       height() - bottom_edge->height(),
    384       width() - bottom_left_corner->width() - bottom_right_corner->width(),
    385       bottom_edge->height());
    386   canvas->DrawImageInt(*bottom_left_corner, 0,
    387                        height() - bottom_left_corner->height());
    388 
    389   // Left.
    390   canvas->TileImageInt(*left_edge, 0, top_left_corner->height(),
    391       left_edge->width(),
    392       height() - top_left_corner->height() - bottom_left_corner->height());
    393 }
    394 
    395 void AppPanelBrowserFrameView::PaintMaximizedFrameBorder(gfx::Canvas* canvas) {
    396   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    397 
    398   gfx::ImageSkia* frame_image = rb.GetImageSkiaNamed(IDR_FRAME_APP_PANEL);
    399   canvas->TileImageInt(*frame_image, 0, FrameBorderThickness(), width(),
    400                        frame_image->height());
    401 
    402   // The bottom of the titlebar actually comes from the top of the Client Edge
    403   // graphic, with the actual client edge clipped off the bottom.
    404   gfx::ImageSkia* titlebar_bottom = rb.GetImageSkiaNamed(IDR_APP_TOP_CENTER);
    405   int edge_height = titlebar_bottom->height() - kClientEdgeThickness;
    406   canvas->TileImageInt(*titlebar_bottom, 0,
    407                        frame()->client_view()->y() - edge_height,
    408                        width(), edge_height);
    409 }
    410 
    411 void AppPanelBrowserFrameView::PaintTitleBar(gfx::Canvas* canvas) {
    412   // The window icon is painted by the TabIconView.
    413   views::WidgetDelegate* d = frame()->widget_delegate();
    414   canvas->DrawStringInt(d->GetWindowTitle(), BrowserFrame::GetTitleFont(),
    415       SK_ColorBLACK, GetMirroredXForRect(title_bounds_), title_bounds_.y(),
    416       title_bounds_.width(), title_bounds_.height());
    417 }
    418 
    419 void AppPanelBrowserFrameView::PaintRestoredClientEdge(gfx::Canvas* canvas) {
    420   gfx::Rect client_area_bounds = CalculateClientAreaBounds(width(), height());
    421   int client_area_top = client_area_bounds.y();
    422 
    423   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    424   gfx::ImageSkia* top_left = rb.GetImageSkiaNamed(IDR_APP_TOP_LEFT);
    425   gfx::ImageSkia* top = rb.GetImageSkiaNamed(IDR_APP_TOP_CENTER);
    426   gfx::ImageSkia* top_right = rb.GetImageSkiaNamed(IDR_APP_TOP_RIGHT);
    427   gfx::ImageSkia* right = rb.GetImageSkiaNamed(IDR_CONTENT_RIGHT_SIDE);
    428   gfx::ImageSkia* bottom_right =
    429       rb.GetImageSkiaNamed(IDR_CONTENT_BOTTOM_RIGHT_CORNER);
    430   gfx::ImageSkia* bottom = rb.GetImageSkiaNamed(IDR_CONTENT_BOTTOM_CENTER);
    431   gfx::ImageSkia* bottom_left =
    432       rb.GetImageSkiaNamed(IDR_CONTENT_BOTTOM_LEFT_CORNER);
    433   gfx::ImageSkia* left = rb.GetImageSkiaNamed(IDR_CONTENT_LEFT_SIDE);
    434 
    435   // Top.
    436   int top_edge_y = client_area_top - top->height();
    437   canvas->DrawImageInt(*top_left, client_area_bounds.x() - top_left->width(),
    438                        top_edge_y);
    439   canvas->TileImageInt(*top, client_area_bounds.x(), top_edge_y,
    440                        client_area_bounds.width(), top->height());
    441   canvas->DrawImageInt(*top_right, client_area_bounds.right(), top_edge_y);
    442 
    443   // Right.
    444   int client_area_bottom =
    445       std::max(client_area_top, client_area_bounds.bottom());
    446   int client_area_height = client_area_bottom - client_area_top;
    447   canvas->TileImageInt(*right, client_area_bounds.right(), client_area_top,
    448                        right->width(), client_area_height);
    449 
    450   // Bottom.
    451   canvas->DrawImageInt(*bottom_right, client_area_bounds.right(),
    452                        client_area_bottom);
    453   canvas->TileImageInt(*bottom, client_area_bounds.x(), client_area_bottom,
    454                        client_area_bounds.width(), bottom_right->height());
    455   canvas->DrawImageInt(*bottom_left,
    456       client_area_bounds.x() - bottom_left->width(), client_area_bottom);
    457 
    458   // Left.
    459   canvas->TileImageInt(*left, client_area_bounds.x() - left->width(),
    460       client_area_top, left->width(), client_area_height);
    461 
    462   // Draw the color to fill in the edges.
    463   canvas->DrawRect(gfx::Rect(
    464       client_area_bounds.x() - kClientEdgeThickness,
    465       client_area_top - kClientEdgeThickness,
    466       client_area_bounds.width() + kClientEdgeThickness,
    467       client_area_bottom - client_area_top + kClientEdgeThickness),
    468       views::kClientEdgeColor);
    469 }
    470 
    471 void AppPanelBrowserFrameView::LayoutWindowControls() {
    472   close_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT,
    473                                    views::ImageButton::ALIGN_BOTTOM);
    474   bool is_maximized = frame()->IsMaximized();
    475   // There should always be the same number of non-border pixels visible to the
    476   // side of the close button.  In maximized mode we extend the button to the
    477   // screen corner to obey Fitts' Law.
    478   int right_extra_width = is_maximized ? kCloseButtonFrameBorderSpacing : 0;
    479   gfx::Size close_button_size = close_button_->GetPreferredSize();
    480   int close_button_y =
    481       (NonClientTopBorderHeight() - close_button_size.height()) / 2;
    482   int top_extra_height = is_maximized ? close_button_y : 0;
    483   close_button_->SetBounds(width() - FrameBorderThickness() -
    484       kCloseButtonFrameBorderSpacing - close_button_size.width(),
    485       close_button_y - top_extra_height,
    486       close_button_size.width() + right_extra_width,
    487       close_button_size.height() + top_extra_height);
    488 }
    489 
    490 void AppPanelBrowserFrameView::LayoutTitleBar() {
    491   // Size the icon first; the window title is based on the icon position.
    492   gfx::Rect icon_bounds(IconBounds());
    493   window_icon_->SetBoundsRect(icon_bounds);
    494 
    495   // Size the title.
    496   int title_x = icon_bounds.right() + kIconTitleSpacing;
    497   int title_height = BrowserFrame::GetTitleFont().GetHeight();
    498   // We bias the title position so that when the difference between the icon
    499   // and title heights is odd, the extra pixel of the title is above the
    500   // vertical midline rather than below.  This compensates for how the icon is
    501   // already biased downwards (see IconBounds()) and helps prevent descenders
    502   // on the title from overlapping the 3D edge at the bottom of the titlebar.
    503   title_bounds_.SetRect(title_x,
    504       icon_bounds.y() + ((icon_bounds.height() - title_height - 1) / 2),
    505       std::max(0, close_button_->x() - kTitleCloseButtonSpacing - title_x),
    506       title_height);
    507 }
    508 
    509 gfx::Rect AppPanelBrowserFrameView::CalculateClientAreaBounds(int width,
    510     int height) const {
    511   int top_height = NonClientTopBorderHeight();
    512   int border_thickness = NonClientBorderThickness();
    513   return gfx::Rect(border_thickness, top_height,
    514                    std::max(0, width - (2 * border_thickness)),
    515                    std::max(0, height - top_height - border_thickness));
    516 }
    517