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