Home | History | Annotate | Download | only in window
      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 "ui/views/window/custom_frame_view.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/strings/utf_string_conversions.h"
     10 #include "grit/ui_resources.h"
     11 #include "grit/ui_strings.h"
     12 #include "ui/base/hit_test.h"
     13 #include "ui/base/l10n/l10n_util.h"
     14 #include "ui/base/resource/resource_bundle.h"
     15 #include "ui/gfx/canvas.h"
     16 #include "ui/gfx/font.h"
     17 #include "ui/gfx/image/image.h"
     18 #include "ui/gfx/path.h"
     19 #include "ui/views/color_constants.h"
     20 #include "ui/views/controls/button/image_button.h"
     21 #include "ui/views/widget/widget.h"
     22 #include "ui/views/widget/widget_delegate.h"
     23 #include "ui/views/window/client_view.h"
     24 #include "ui/views/window/frame_background.h"
     25 #include "ui/views/window/window_resources.h"
     26 #include "ui/views/window/window_shape.h"
     27 
     28 #if defined(USE_AURA)
     29 #include "ui/views/widget/native_widget_aura.h"
     30 #elif defined(OS_WIN)
     31 #include "ui/views/widget/native_widget_win.h"
     32 #endif
     33 
     34 namespace views {
     35 
     36 namespace {
     37 
     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.
     40 const int kFrameBorderThickness = 4;
     41 // Various edges of the frame border have a 1 px shadow along their edges; in a
     42 // few cases we shift elements based on this amount for visual appeal.
     43 const int kFrameShadowThickness = 1;
     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.)
     49 const 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.
     52 const int kResizeAreaCornerSize = 16;
     53 // The titlebar never shrinks too short to show the caption button plus some
     54 // padding below it.
     55 const int kCaptionButtonHeightWithPadding = 19;
     56 // The titlebar has a 2 px 3D edge along the top and bottom.
     57 const int kTitlebarTopAndBottomEdgeThickness = 2;
     58 // The icon is inset 2 px from the left frame border.
     59 const int kIconLeftSpacing = 2;
     60 // The icon never shrinks below 16 px on a side.
     61 const int kIconMinimumSize = 16;
     62 // The space between the window icon and the title text.
     63 const int kTitleIconOffsetX = 4;
     64 // The space between the title text and the caption buttons.
     65 const int kTitleCaptionSpacing = 5;
     66 
     67 #if defined(USE_AURA)
     68 const SkColor kDefaultColorFrame = SkColorSetRGB(109, 109, 109);
     69 const SkColor kDefaultColorFrameInactive = SkColorSetRGB(176, 176, 176);
     70 #else
     71 const SkColor kDefaultColorFrame = SkColorSetRGB(66, 116, 201);
     72 const SkColor kDefaultColorFrameInactive = SkColorSetRGB(161, 182, 228);
     73 #endif
     74 
     75 const gfx::Font& GetTitleFont() {
     76   static gfx::Font* title_font = NULL;
     77   if (!title_font) {
     78 #if defined(USE_AURA)
     79     title_font = new gfx::Font(NativeWidgetAura::GetWindowTitleFont());
     80 #elif defined(OS_WIN)
     81     title_font = new gfx::Font(NativeWidgetWin::GetWindowTitleFont());
     82 #elif defined(OS_LINUX)
     83     // TODO(ben): need to resolve what font this is.
     84     title_font = new gfx::Font();
     85 #endif
     86   }
     87   return *title_font;
     88 }
     89 
     90 }  // namespace
     91 
     92 ///////////////////////////////////////////////////////////////////////////////
     93 // CustomFrameView, public:
     94 
     95 CustomFrameView::CustomFrameView()
     96     : frame_(NULL),
     97       window_icon_(NULL),
     98       minimize_button_(NULL),
     99       maximize_button_(NULL),
    100       restore_button_(NULL),
    101       close_button_(NULL),
    102       should_show_minmax_buttons_(false),
    103       frame_background_(new FrameBackground()) {
    104 }
    105 
    106 CustomFrameView::~CustomFrameView() {
    107 }
    108 
    109 void CustomFrameView::Init(Widget* frame) {
    110   frame_ = frame;
    111 
    112   close_button_ = new ImageButton(this);
    113   close_button_->SetAccessibleName(
    114       l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE));
    115 
    116   // Close button images will be set in LayoutWindowControls().
    117   AddChildView(close_button_);
    118 
    119   minimize_button_ = InitWindowCaptionButton(IDS_APP_ACCNAME_MINIMIZE,
    120       IDR_MINIMIZE, IDR_MINIMIZE_H, IDR_MINIMIZE_P);
    121 
    122   maximize_button_ = InitWindowCaptionButton(IDS_APP_ACCNAME_MAXIMIZE,
    123       IDR_MAXIMIZE, IDR_MAXIMIZE_H, IDR_MAXIMIZE_P);
    124 
    125   restore_button_ = InitWindowCaptionButton(IDS_APP_ACCNAME_RESTORE,
    126       IDR_RESTORE, IDR_RESTORE_H, IDR_RESTORE_P);
    127 
    128   should_show_minmax_buttons_ = frame_->widget_delegate()->CanMaximize();
    129 
    130   if (frame_->widget_delegate()->ShouldShowWindowIcon()) {
    131     window_icon_ = new ImageButton(this);
    132     AddChildView(window_icon_);
    133   }
    134 }
    135 
    136 ///////////////////////////////////////////////////////////////////////////////
    137 // CustomFrameView, NonClientFrameView implementation:
    138 
    139 gfx::Rect CustomFrameView::GetBoundsForClientView() const {
    140   return client_view_bounds_;
    141 }
    142 
    143 gfx::Rect CustomFrameView::GetWindowBoundsForClientBounds(
    144     const gfx::Rect& client_bounds) const {
    145   int top_height = NonClientTopBorderHeight();
    146   int border_thickness = NonClientBorderThickness();
    147   return gfx::Rect(std::max(0, client_bounds.x() - border_thickness),
    148                    std::max(0, client_bounds.y() - top_height),
    149                    client_bounds.width() + (2 * border_thickness),
    150                    client_bounds.height() + top_height + border_thickness);
    151 }
    152 
    153 int CustomFrameView::NonClientHitTest(const gfx::Point& point) {
    154   // Sanity check.
    155   if (!bounds().Contains(point))
    156     return HTNOWHERE;
    157 
    158   int frame_component = frame_->client_view()->NonClientHitTest(point);
    159 
    160   // See if we're in the sysmenu region.  (We check the ClientView first to be
    161   // consistent with OpaqueBrowserFrameView; it's not really necessary here.)
    162   gfx::Rect sysmenu_rect(IconBounds());
    163   // In maximized mode we extend the rect to the screen corner to take advantage
    164   // of Fitts' Law.
    165   if (frame_->IsMaximized())
    166     sysmenu_rect.SetRect(0, 0, sysmenu_rect.right(), sysmenu_rect.bottom());
    167   sysmenu_rect.set_x(GetMirroredXForRect(sysmenu_rect));
    168   if (sysmenu_rect.Contains(point))
    169     return (frame_component == HTCLIENT) ? HTCLIENT : HTSYSMENU;
    170 
    171   if (frame_component != HTNOWHERE)
    172     return frame_component;
    173 
    174   // Then see if the point is within any of the window controls.
    175   if (close_button_->GetMirroredBounds().Contains(point))
    176     return HTCLOSE;
    177   if (restore_button_->GetMirroredBounds().Contains(point))
    178     return HTMAXBUTTON;
    179   if (maximize_button_->GetMirroredBounds().Contains(point))
    180     return HTMAXBUTTON;
    181   if (minimize_button_->GetMirroredBounds().Contains(point))
    182     return HTMINBUTTON;
    183   if (window_icon_ && window_icon_->GetMirroredBounds().Contains(point))
    184     return HTSYSMENU;
    185 
    186   int window_component = GetHTComponentForFrame(point, FrameBorderThickness(),
    187       NonClientBorderThickness(), kResizeAreaCornerSize, kResizeAreaCornerSize,
    188       frame_->widget_delegate()->CanResize());
    189   // Fall back to the caption if no other component matches.
    190   return (window_component == HTNOWHERE) ? HTCAPTION : window_component;
    191 }
    192 
    193 void CustomFrameView::GetWindowMask(const gfx::Size& size,
    194                                     gfx::Path* window_mask) {
    195   DCHECK(window_mask);
    196   if (frame_->IsMaximized())
    197     return;
    198 
    199   GetDefaultWindowMask(size, window_mask);
    200 }
    201 
    202 void CustomFrameView::ResetWindowControls() {
    203   restore_button_->SetState(CustomButton::STATE_NORMAL);
    204   minimize_button_->SetState(CustomButton::STATE_NORMAL);
    205   maximize_button_->SetState(CustomButton::STATE_NORMAL);
    206   // The close button isn't affected by this constraint.
    207 }
    208 
    209 void CustomFrameView::UpdateWindowIcon() {
    210   if (window_icon_)
    211     window_icon_->SchedulePaint();
    212 }
    213 
    214 void CustomFrameView::UpdateWindowTitle() {
    215   SchedulePaintInRect(title_bounds_);
    216 }
    217 
    218 ///////////////////////////////////////////////////////////////////////////////
    219 // CustomFrameView, View overrides:
    220 
    221 void CustomFrameView::OnPaint(gfx::Canvas* canvas) {
    222   if (frame_->IsMaximized())
    223     PaintMaximizedFrameBorder(canvas);
    224   else
    225     PaintRestoredFrameBorder(canvas);
    226   PaintTitleBar(canvas);
    227   if (ShouldShowClientEdge())
    228     PaintRestoredClientEdge(canvas);
    229 }
    230 
    231 void CustomFrameView::Layout() {
    232   LayoutWindowControls();
    233   LayoutTitleBar();
    234   LayoutClientView();
    235 }
    236 
    237 gfx::Size CustomFrameView::GetPreferredSize() {
    238   return frame_->non_client_view()->GetWindowBoundsForClientBounds(
    239       gfx::Rect(frame_->client_view()->GetPreferredSize())).size();
    240 }
    241 
    242 gfx::Size CustomFrameView::GetMinimumSize() {
    243   return frame_->non_client_view()->GetWindowBoundsForClientBounds(
    244       gfx::Rect(frame_->client_view()->GetMinimumSize())).size();
    245 }
    246 
    247 gfx::Size CustomFrameView::GetMaximumSize() {
    248   gfx::Size max_size = frame_->client_view()->GetMaximumSize();
    249   gfx::Size converted_size =
    250       frame_->non_client_view()->GetWindowBoundsForClientBounds(
    251           gfx::Rect(max_size)).size();
    252   return gfx::Size(max_size.width() == 0 ? 0 : converted_size.width(),
    253                    max_size.height() == 0 ? 0 : converted_size.height());
    254 }
    255 
    256 ///////////////////////////////////////////////////////////////////////////////
    257 // CustomFrameView, ButtonListener implementation:
    258 
    259 void CustomFrameView::ButtonPressed(Button* sender, const ui::Event& event) {
    260   if (sender == close_button_)
    261     frame_->Close();
    262   else if (sender == minimize_button_)
    263     frame_->Minimize();
    264   else if (sender == maximize_button_)
    265     frame_->Maximize();
    266   else if (sender == restore_button_)
    267     frame_->Restore();
    268 }
    269 
    270 ///////////////////////////////////////////////////////////////////////////////
    271 // CustomFrameView, private:
    272 
    273 int CustomFrameView::FrameBorderThickness() const {
    274   return frame_->IsMaximized() ? 0 : kFrameBorderThickness;
    275 }
    276 
    277 int CustomFrameView::NonClientBorderThickness() const {
    278   // In maximized mode, we don't show a client edge.
    279   return FrameBorderThickness() +
    280       (ShouldShowClientEdge() ? kClientEdgeThickness : 0);
    281 }
    282 
    283 int CustomFrameView::NonClientTopBorderHeight() const {
    284   return std::max(FrameBorderThickness() + IconSize(),
    285                   CaptionButtonY() + kCaptionButtonHeightWithPadding) +
    286       TitlebarBottomThickness();
    287 }
    288 
    289 int CustomFrameView::CaptionButtonY() const {
    290   // Maximized buttons start at window top so that even if their images aren't
    291   // drawn flush with the screen edge, they still obey Fitts' Law.
    292   return frame_->IsMaximized() ? FrameBorderThickness() : kFrameShadowThickness;
    293 }
    294 
    295 int CustomFrameView::TitlebarBottomThickness() const {
    296   return kTitlebarTopAndBottomEdgeThickness +
    297       (ShouldShowClientEdge() ? kClientEdgeThickness : 0);
    298 }
    299 
    300 int CustomFrameView::IconSize() const {
    301 #if defined(OS_WIN)
    302   // This metric scales up if either the titlebar height or the titlebar font
    303   // size are increased.
    304   return GetSystemMetrics(SM_CYSMICON);
    305 #else
    306   return std::max(GetTitleFont().GetHeight(), kIconMinimumSize);
    307 #endif
    308 }
    309 
    310 gfx::Rect CustomFrameView::IconBounds() const {
    311   int size = IconSize();
    312   int frame_thickness = FrameBorderThickness();
    313   // Our frame border has a different "3D look" than Windows'.  Theirs has a
    314   // more complex gradient on the top that they push their icon/title below;
    315   // then the maximized window cuts this off and the icon/title are centered
    316   // in the remaining space.  Because the apparent shape of our border is
    317   // simpler, using the same positioning makes things look slightly uncentered
    318   // with restored windows, so when the window is restored, instead of
    319   // calculating the remaining space from below the frame border, we calculate
    320   // from below the 3D edge.
    321   int unavailable_px_at_top = frame_->IsMaximized() ?
    322       frame_thickness : kTitlebarTopAndBottomEdgeThickness;
    323   // When the icon is shorter than the minimum space we reserve for the caption
    324   // button, we vertically center it.  We want to bias rounding to put extra
    325   // space above the icon, since the 3D edge (+ client edge, for restored
    326   // windows) below looks (to the eye) more like additional space than does the
    327   // 3D edge (or nothing at all, for maximized windows) above; hence the +1.
    328   int y = unavailable_px_at_top + (NonClientTopBorderHeight() -
    329       unavailable_px_at_top - size - TitlebarBottomThickness() + 1) / 2;
    330   return gfx::Rect(frame_thickness + kIconLeftSpacing, y, size, size);
    331 }
    332 
    333 bool CustomFrameView::ShouldShowClientEdge() const {
    334   return !frame_->IsMaximized();
    335 }
    336 
    337 void CustomFrameView::PaintRestoredFrameBorder(gfx::Canvas* canvas) {
    338   frame_background_->set_frame_color(GetFrameColor());
    339   const gfx::ImageSkia* frame_image = GetFrameImage();
    340   frame_background_->set_theme_image(frame_image);
    341   frame_background_->set_top_area_height(frame_image->height());
    342 
    343   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    344 
    345   frame_background_->SetCornerImages(
    346       rb.GetImageNamed(IDR_WINDOW_TOP_LEFT_CORNER).ToImageSkia(),
    347       rb.GetImageNamed(IDR_WINDOW_TOP_RIGHT_CORNER).ToImageSkia(),
    348       rb.GetImageNamed(IDR_WINDOW_BOTTOM_LEFT_CORNER).ToImageSkia(),
    349       rb.GetImageNamed(IDR_WINDOW_BOTTOM_RIGHT_CORNER).ToImageSkia());
    350   frame_background_->SetSideImages(
    351       rb.GetImageNamed(IDR_WINDOW_LEFT_SIDE).ToImageSkia(),
    352       rb.GetImageNamed(IDR_WINDOW_TOP_CENTER).ToImageSkia(),
    353       rb.GetImageNamed(IDR_WINDOW_RIGHT_SIDE).ToImageSkia(),
    354       rb.GetImageNamed(IDR_WINDOW_BOTTOM_CENTER).ToImageSkia());
    355 
    356   frame_background_->PaintRestored(canvas, this);
    357 }
    358 
    359 void CustomFrameView::PaintMaximizedFrameBorder(gfx::Canvas* canvas) {
    360   const gfx::ImageSkia* frame_image = GetFrameImage();
    361   frame_background_->set_theme_image(frame_image);
    362   frame_background_->set_top_area_height(frame_image->height());
    363   frame_background_->PaintMaximized(canvas, this);
    364 
    365   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    366 
    367   // TODO(jamescook): Migrate this into FrameBackground.
    368   // The bottom of the titlebar actually comes from the top of the Client Edge
    369   // graphic, with the actual client edge clipped off the bottom.
    370   const gfx::ImageSkia* titlebar_bottom = rb.GetImageNamed(
    371       IDR_APP_TOP_CENTER).ToImageSkia();
    372   int edge_height = titlebar_bottom->height() -
    373       (ShouldShowClientEdge() ? kClientEdgeThickness : 0);
    374   canvas->TileImageInt(*titlebar_bottom, 0,
    375       frame_->client_view()->y() - edge_height, width(), edge_height);
    376 }
    377 
    378 void CustomFrameView::PaintTitleBar(gfx::Canvas* canvas) {
    379   WidgetDelegate* delegate = frame_->widget_delegate();
    380 
    381   // It seems like in some conditions we can be asked to paint after the window
    382   // that contains us is WM_DESTROYed. At this point, our delegate is NULL. The
    383   // correct long term fix may be to shut down the RootView in WM_DESTROY.
    384   if (!delegate)
    385     return;
    386 
    387   canvas->DrawStringInt(delegate->GetWindowTitle(), GetTitleFont(),
    388                         SK_ColorWHITE, GetMirroredXForRect(title_bounds_),
    389                         title_bounds_.y(), title_bounds_.width(),
    390                         title_bounds_.height());
    391 }
    392 
    393 void CustomFrameView::PaintRestoredClientEdge(gfx::Canvas* canvas) {
    394   gfx::Rect client_area_bounds = frame_->client_view()->bounds();
    395   int client_area_top = client_area_bounds.y();
    396 
    397   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    398   const gfx::ImageSkia* top_left = rb.GetImageNamed(
    399       IDR_APP_TOP_LEFT).ToImageSkia();
    400   const gfx::ImageSkia* top = rb.GetImageNamed(
    401       IDR_APP_TOP_CENTER).ToImageSkia();
    402   const gfx::ImageSkia* top_right = rb.GetImageNamed(
    403       IDR_APP_TOP_RIGHT).ToImageSkia();
    404   const gfx::ImageSkia* right = rb.GetImageNamed(
    405       IDR_CONTENT_RIGHT_SIDE).ToImageSkia();
    406   const gfx::ImageSkia* bottom_right = rb.GetImageNamed(
    407       IDR_CONTENT_BOTTOM_RIGHT_CORNER).ToImageSkia();
    408   const gfx::ImageSkia* bottom = rb.GetImageNamed(
    409       IDR_CONTENT_BOTTOM_CENTER).ToImageSkia();
    410   const gfx::ImageSkia* bottom_left = rb.GetImageNamed(
    411       IDR_CONTENT_BOTTOM_LEFT_CORNER).ToImageSkia();
    412   const gfx::ImageSkia* left = rb.GetImageNamed(
    413       IDR_CONTENT_LEFT_SIDE).ToImageSkia();
    414 
    415   // Top.
    416   int top_edge_y = client_area_top - top->height();
    417   canvas->DrawImageInt(*top_left, client_area_bounds.x() - top_left->width(),
    418                        top_edge_y);
    419   canvas->TileImageInt(*top, client_area_bounds.x(), top_edge_y,
    420                        client_area_bounds.width(), top->height());
    421   canvas->DrawImageInt(*top_right, client_area_bounds.right(), top_edge_y);
    422 
    423   // Right.
    424   int client_area_bottom =
    425       std::max(client_area_top, client_area_bounds.bottom());
    426   int client_area_height = client_area_bottom - client_area_top;
    427   canvas->TileImageInt(*right, client_area_bounds.right(), client_area_top,
    428                        right->width(), client_area_height);
    429 
    430   // Bottom.
    431   canvas->DrawImageInt(*bottom_right, client_area_bounds.right(),
    432                        client_area_bottom);
    433   canvas->TileImageInt(*bottom, client_area_bounds.x(), client_area_bottom,
    434                        client_area_bounds.width(), bottom_right->height());
    435   canvas->DrawImageInt(*bottom_left,
    436       client_area_bounds.x() - bottom_left->width(), client_area_bottom);
    437 
    438   // Left.
    439   canvas->TileImageInt(*left, client_area_bounds.x() - left->width(),
    440       client_area_top, left->width(), client_area_height);
    441 
    442   // Draw the color to fill in the edges.
    443   canvas->FillRect(gfx::Rect(client_area_bounds.x() - 1, client_area_top - 1,
    444       client_area_bounds.width() + 1, client_area_bottom - client_area_top + 1),
    445       kClientEdgeColor);
    446 }
    447 
    448 SkColor CustomFrameView::GetFrameColor() const {
    449   return frame_->IsActive() ? kDefaultColorFrame : kDefaultColorFrameInactive;
    450 }
    451 
    452 const gfx::ImageSkia* CustomFrameView::GetFrameImage() const {
    453   return ui::ResourceBundle::GetSharedInstance().GetImageNamed(
    454       frame_->IsActive() ? IDR_FRAME : IDR_FRAME_INACTIVE).ToImageSkia();
    455 }
    456 
    457 void CustomFrameView::LayoutWindowControls() {
    458   close_button_->SetImageAlignment(ImageButton::ALIGN_LEFT,
    459                                    ImageButton::ALIGN_BOTTOM);
    460   int caption_y = CaptionButtonY();
    461   bool is_maximized = frame_->IsMaximized();
    462   // There should always be the same number of non-shadow pixels visible to the
    463   // side of the caption buttons.  In maximized mode we extend the rightmost
    464   // button to the screen corner to obey Fitts' Law.
    465   int right_extra_width = is_maximized ?
    466       (kFrameBorderThickness - kFrameShadowThickness) : 0;
    467   gfx::Size close_button_size = close_button_->GetPreferredSize();
    468   close_button_->SetBounds(width() - FrameBorderThickness() -
    469       right_extra_width - close_button_size.width(), caption_y,
    470       close_button_size.width() + right_extra_width,
    471       close_button_size.height());
    472 
    473   // When the window is restored, we show a maximized button; otherwise, we show
    474   // a restore button.
    475   bool is_restored = !is_maximized && !frame_->IsMinimized();
    476   ImageButton* invisible_button = is_restored ? restore_button_
    477                                               : maximize_button_;
    478   invisible_button->SetVisible(false);
    479 
    480   ImageButton* visible_button = is_restored ? maximize_button_
    481                                             : restore_button_;
    482   FramePartImage normal_part, hot_part, pushed_part;
    483   if (should_show_minmax_buttons_) {
    484     visible_button->SetVisible(true);
    485     visible_button->SetImageAlignment(ImageButton::ALIGN_LEFT,
    486                                       ImageButton::ALIGN_BOTTOM);
    487     gfx::Size visible_button_size = visible_button->GetPreferredSize();
    488     visible_button->SetBounds(close_button_->x() - visible_button_size.width(),
    489                               caption_y, visible_button_size.width(),
    490                               visible_button_size.height());
    491 
    492     minimize_button_->SetVisible(true);
    493     minimize_button_->SetImageAlignment(ImageButton::ALIGN_LEFT,
    494                                         ImageButton::ALIGN_BOTTOM);
    495     gfx::Size minimize_button_size = minimize_button_->GetPreferredSize();
    496     minimize_button_->SetBounds(
    497         visible_button->x() - minimize_button_size.width(), caption_y,
    498         minimize_button_size.width(),
    499         minimize_button_size.height());
    500 
    501     normal_part = IDR_CLOSE;
    502     hot_part = IDR_CLOSE_H;
    503     pushed_part = IDR_CLOSE_P;
    504   } else {
    505     visible_button->SetVisible(false);
    506     minimize_button_->SetVisible(false);
    507 
    508     normal_part = IDR_CLOSE_SA;
    509     hot_part = IDR_CLOSE_SA_H;
    510     pushed_part = IDR_CLOSE_SA_P;
    511   }
    512 
    513   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    514 
    515   close_button_->SetImage(CustomButton::STATE_NORMAL,
    516                           rb.GetImageNamed(normal_part).ToImageSkia());
    517   close_button_->SetImage(CustomButton::STATE_HOVERED,
    518                           rb.GetImageNamed(hot_part).ToImageSkia());
    519   close_button_->SetImage(CustomButton::STATE_PRESSED,
    520                           rb.GetImageNamed(pushed_part).ToImageSkia());
    521 }
    522 
    523 void CustomFrameView::LayoutTitleBar() {
    524   // The window title position is calculated based on the icon position, even
    525   // when there is no icon.
    526   gfx::Rect icon_bounds(IconBounds());
    527   bool show_window_icon = window_icon_ != NULL;
    528   if (show_window_icon)
    529     window_icon_->SetBoundsRect(icon_bounds);
    530 
    531   // The offset between the window left edge and the title text.
    532   int title_x = show_window_icon ? icon_bounds.right() + kTitleIconOffsetX
    533                                  : icon_bounds.x();
    534   int title_height = GetTitleFont().GetHeight();
    535   // We bias the title position so that when the difference between the icon and
    536   // title heights is odd, the extra pixel of the title is above the vertical
    537   // midline rather than below.  This compensates for how the icon is already
    538   // biased downwards (see IconBounds()) and helps prevent descenders on the
    539   // title from overlapping the 3D edge at the bottom of the titlebar.
    540   title_bounds_.SetRect(title_x,
    541       icon_bounds.y() + ((icon_bounds.height() - title_height - 1) / 2),
    542       std::max(0, (should_show_minmax_buttons_ ?
    543           minimize_button_->x() : close_button_->x()) - kTitleCaptionSpacing -
    544       title_x), title_height);
    545 }
    546 
    547 void CustomFrameView::LayoutClientView() {
    548   int top_height = NonClientTopBorderHeight();
    549   int border_thickness = NonClientBorderThickness();
    550   client_view_bounds_.SetRect(border_thickness, top_height,
    551       std::max(0, width() - (2 * border_thickness)),
    552       std::max(0, height() - top_height - border_thickness));
    553 }
    554 
    555 ImageButton* CustomFrameView::InitWindowCaptionButton(
    556     int accessibility_string_id,
    557     int normal_image_id,
    558     int hot_image_id,
    559     int pushed_image_id) {
    560   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    561   ImageButton* button = new ImageButton(this);
    562   button->SetAccessibleName(l10n_util::GetStringUTF16(accessibility_string_id));
    563   button->SetImage(CustomButton::STATE_NORMAL,
    564                    rb.GetImageNamed(normal_image_id).ToImageSkia());
    565   button->SetImage(CustomButton::STATE_HOVERED,
    566                    rb.GetImageNamed(hot_image_id).ToImageSkia());
    567   button->SetImage(CustomButton::STATE_PRESSED,
    568                    rb.GetImageNamed(pushed_image_id).ToImageSkia());
    569   AddChildView(button);
    570   return button;
    571 }
    572 
    573 }  // namespace views
    574