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