| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2009 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/views/default_non_client_view.h" | |
| 6 | |
| 7 #include "base/win_util.h" | |
| 8 #include "chrome/common/gfx/path.h" | |
| 9 #include "chrome/common/gfx/chrome_font.h" | |
| 10 #include "chrome/common/resource_bundle.h" | |
| 11 #include "chrome/common/win_util.h" | |
| 12 #include "chrome/views/client_view.h" | |
| 13 #include "grit/theme_resources.h" | |
| 14 | |
| 15 namespace views { | |
| 16 | |
| 17 // An enumeration of bitmap resources used by this window. | |
| 18 enum { | |
| 19 FRAME_PART_BITMAP_FIRST = 0, // Must be first. | |
| 20 | |
| 21 // Window Controls. | |
| 22 FRAME_CLOSE_BUTTON_ICON, | |
| 23 FRAME_CLOSE_BUTTON_ICON_H, | |
| 24 FRAME_CLOSE_BUTTON_ICON_P, | |
| 25 FRAME_CLOSE_BUTTON_ICON_SA, | |
| 26 FRAME_CLOSE_BUTTON_ICON_SA_H, | |
| 27 FRAME_CLOSE_BUTTON_ICON_SA_P, | |
| 28 FRAME_RESTORE_BUTTON_ICON, | |
| 29 FRAME_RESTORE_BUTTON_ICON_H, | |
| 30 FRAME_RESTORE_BUTTON_ICON_P, | |
| 31 FRAME_MAXIMIZE_BUTTON_ICON, | |
| 32 FRAME_MAXIMIZE_BUTTON_ICON_H, | |
| 33 FRAME_MAXIMIZE_BUTTON_ICON_P, | |
| 34 FRAME_MINIMIZE_BUTTON_ICON, | |
| 35 FRAME_MINIMIZE_BUTTON_ICON_H, | |
| 36 FRAME_MINIMIZE_BUTTON_ICON_P, | |
| 37 | |
| 38 // Window Frame Border. | |
| 39 FRAME_BOTTOM_EDGE, | |
| 40 FRAME_BOTTOM_LEFT_CORNER, | |
| 41 FRAME_BOTTOM_RIGHT_CORNER, | |
| 42 FRAME_LEFT_EDGE, | |
| 43 FRAME_RIGHT_EDGE, | |
| 44 FRAME_TOP_EDGE, | |
| 45 FRAME_TOP_LEFT_CORNER, | |
| 46 FRAME_TOP_RIGHT_CORNER, | |
| 47 | |
| 48 // Client Edge Border. | |
| 49 FRAME_CLIENT_EDGE_TOP_LEFT, | |
| 50 FRAME_CLIENT_EDGE_TOP, | |
| 51 FRAME_CLIENT_EDGE_TOP_RIGHT, | |
| 52 FRAME_CLIENT_EDGE_RIGHT, | |
| 53 FRAME_CLIENT_EDGE_BOTTOM_RIGHT, | |
| 54 FRAME_CLIENT_EDGE_BOTTOM, | |
| 55 FRAME_CLIENT_EDGE_BOTTOM_LEFT, | |
| 56 FRAME_CLIENT_EDGE_LEFT, | |
| 57 | |
| 58 FRAME_PART_BITMAP_COUNT // Must be last. | |
| 59 }; | |
| 60 | |
| 61 class ActiveWindowResources : public WindowResources { | |
| 62 public: | |
| 63 ActiveWindowResources() { | |
| 64 InitClass(); | |
| 65 } | |
| 66 virtual ~ActiveWindowResources() { | |
| 67 } | |
| 68 | |
| 69 // WindowResources implementation: | |
| 70 virtual SkBitmap* GetPartBitmap(FramePartBitmap part) const { | |
| 71 return standard_frame_bitmaps_[part]; | |
| 72 } | |
| 73 | |
| 74 private: | |
| 75 static void InitClass() { | |
| 76 static bool initialized = false; | |
| 77 if (!initialized) { | |
| 78 static const int kFramePartBitmapIds[] = { | |
| 79 0, | |
| 80 IDR_CLOSE, IDR_CLOSE_H, IDR_CLOSE_P, | |
| 81 IDR_CLOSE_SA, IDR_CLOSE_SA_H, IDR_CLOSE_SA_P, | |
| 82 IDR_RESTORE, IDR_RESTORE_H, IDR_RESTORE_P, | |
| 83 IDR_MAXIMIZE, IDR_MAXIMIZE_H, IDR_MAXIMIZE_P, | |
| 84 IDR_MINIMIZE, IDR_MINIMIZE_H, IDR_MINIMIZE_P, | |
| 85 IDR_WINDOW_BOTTOM_CENTER, IDR_WINDOW_BOTTOM_LEFT_CORNER, | |
| 86 IDR_WINDOW_BOTTOM_RIGHT_CORNER, IDR_WINDOW_LEFT_SIDE, | |
| 87 IDR_WINDOW_RIGHT_SIDE, IDR_WINDOW_TOP_CENTER, | |
| 88 IDR_WINDOW_TOP_LEFT_CORNER, IDR_WINDOW_TOP_RIGHT_CORNER, | |
| 89 IDR_APP_TOP_LEFT, IDR_APP_TOP_CENTER, IDR_APP_TOP_RIGHT, | |
| 90 IDR_CONTENT_RIGHT_SIDE, IDR_CONTENT_BOTTOM_RIGHT_CORNER, | |
| 91 IDR_CONTENT_BOTTOM_CENTER, IDR_CONTENT_BOTTOM_LEFT_CORNER, | |
| 92 IDR_CONTENT_LEFT_SIDE, | |
| 93 0 | |
| 94 }; | |
| 95 | |
| 96 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 97 for (int i = 0; i < FRAME_PART_BITMAP_COUNT; ++i) { | |
| 98 int id = kFramePartBitmapIds[i]; | |
| 99 if (id != 0) | |
| 100 standard_frame_bitmaps_[i] = rb.GetBitmapNamed(id); | |
| 101 } | |
| 102 initialized = true; | |
| 103 } | |
| 104 } | |
| 105 | |
| 106 static SkBitmap* standard_frame_bitmaps_[FRAME_PART_BITMAP_COUNT]; | |
| 107 static ChromeFont title_font_; | |
| 108 | |
| 109 DISALLOW_EVIL_CONSTRUCTORS(ActiveWindowResources); | |
| 110 }; | |
| 111 | |
| 112 class InactiveWindowResources : public WindowResources { | |
| 113 public: | |
| 114 InactiveWindowResources() { | |
| 115 InitClass(); | |
| 116 } | |
| 117 virtual ~InactiveWindowResources() { | |
| 118 } | |
| 119 | |
| 120 // WindowResources implementation: | |
| 121 virtual SkBitmap* GetPartBitmap(FramePartBitmap part) const { | |
| 122 return standard_frame_bitmaps_[part]; | |
| 123 } | |
| 124 | |
| 125 private: | |
| 126 static void InitClass() { | |
| 127 static bool initialized = false; | |
| 128 if (!initialized) { | |
| 129 static const int kFramePartBitmapIds[] = { | |
| 130 0, | |
| 131 IDR_CLOSE, IDR_CLOSE_H, IDR_CLOSE_P, | |
| 132 IDR_CLOSE_SA, IDR_CLOSE_SA_H, IDR_CLOSE_SA_P, | |
| 133 IDR_RESTORE, IDR_RESTORE_H, IDR_RESTORE_P, | |
| 134 IDR_MAXIMIZE, IDR_MAXIMIZE_H, IDR_MAXIMIZE_P, | |
| 135 IDR_MINIMIZE, IDR_MINIMIZE_H, IDR_MINIMIZE_P, | |
| 136 IDR_DEWINDOW_BOTTOM_CENTER, IDR_DEWINDOW_BOTTOM_LEFT_CORNER, | |
| 137 IDR_DEWINDOW_BOTTOM_RIGHT_CORNER, IDR_DEWINDOW_LEFT_SIDE, | |
| 138 IDR_DEWINDOW_RIGHT_SIDE, IDR_DEWINDOW_TOP_CENTER, | |
| 139 IDR_DEWINDOW_TOP_LEFT_CORNER, IDR_DEWINDOW_TOP_RIGHT_CORNER, | |
| 140 IDR_APP_TOP_LEFT, IDR_APP_TOP_CENTER, IDR_APP_TOP_RIGHT, | |
| 141 IDR_CONTENT_RIGHT_SIDE, IDR_CONTENT_BOTTOM_RIGHT_CORNER, | |
| 142 IDR_CONTENT_BOTTOM_CENTER, IDR_CONTENT_BOTTOM_LEFT_CORNER, | |
| 143 IDR_CONTENT_LEFT_SIDE, | |
| 144 0 | |
| 145 }; | |
| 146 | |
| 147 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 148 for (int i = 0; i < FRAME_PART_BITMAP_COUNT; ++i) { | |
| 149 int id = kFramePartBitmapIds[i]; | |
| 150 if (id != 0) | |
| 151 standard_frame_bitmaps_[i] = rb.GetBitmapNamed(id); | |
| 152 } | |
| 153 initialized = true; | |
| 154 } | |
| 155 } | |
| 156 | |
| 157 static SkBitmap* standard_frame_bitmaps_[FRAME_PART_BITMAP_COUNT]; | |
| 158 | |
| 159 DISALLOW_EVIL_CONSTRUCTORS(InactiveWindowResources); | |
| 160 }; | |
| 161 | |
| 162 // static | |
| 163 SkBitmap* ActiveWindowResources::standard_frame_bitmaps_[]; | |
| 164 SkBitmap* InactiveWindowResources::standard_frame_bitmaps_[]; | |
| 165 | |
| 166 // static | |
| 167 WindowResources* DefaultNonClientView::active_resources_ = NULL; | |
| 168 WindowResources* DefaultNonClientView::inactive_resources_ = NULL; | |
| 169 ChromeFont DefaultNonClientView::title_font_; | |
| 170 | |
| 171 namespace { | |
| 172 // The frame border is only visible in restored mode and is hardcoded to 4 px on | |
| 173 // each side regardless of the system window border size. | |
| 174 const int kFrameBorderThickness = 4; | |
| 175 // Various edges of the frame border have a 1 px shadow along their edges; in a | |
| 176 // few cases we shift elements based on this amount for visual appeal. | |
| 177 const int kFrameShadowThickness = 1; | |
| 178 // While resize areas on Windows are normally the same size as the window | |
| 179 // borders, our top area is shrunk by 1 px to make it easier to move the window | |
| 180 // around with our thinner top grabbable strip. (Incidentally, our side and | |
| 181 // bottom resize areas don't match the frame border thickness either -- they | |
| 182 // span the whole nonclient area, so there's no "dead zone" for the mouse.) | |
| 183 const int kTopResizeAdjust = 1; | |
| 184 // In the window corners, the resize areas don't actually expand bigger, but the | |
| 185 // 16 px at the end of each edge triggers diagonal resizing. | |
| 186 const int kResizeAreaCornerSize = 16; | |
| 187 // The titlebar never shrinks to less than 18 px tall, plus the height of the | |
| 188 // frame border and any bottom edge. | |
| 189 const int kTitlebarMinimumHeight = 18; | |
| 190 // The icon is inset 2 px from the left frame border. | |
| 191 const int kIconLeftSpacing = 2; | |
| 192 // The icon takes up 16/25th of the available titlebar height. (This is | |
| 193 // expressed as two ints to avoid precision losses leading to off-by-one pixel | |
| 194 // errors.) | |
| 195 const int kIconHeightFractionNumerator = 16; | |
| 196 const int kIconHeightFractionDenominator = 25; | |
| 197 // The icon never shrinks below 16 px on a side. | |
| 198 const int kIconMinimumSize = 16; | |
| 199 // Because our frame border has a different "3D look" than Windows', with a less | |
| 200 // cluttered top edge, we need to shift the icon up by 1 px in restored mode so | |
| 201 // it looks more centered. | |
| 202 const int kIconRestoredAdjust = 1; | |
| 203 // There is a 4 px gap between the icon and the title text. | |
| 204 const int kIconTitleSpacing = 4; | |
| 205 // The title text starts 2 px below the bottom of the top frame border. | |
| 206 const int kTitleTopSpacing = 2; | |
| 207 // There is a 5 px gap between the title text and the caption buttons. | |
| 208 const int kTitleCaptionSpacing = 5; | |
| 209 // The caption buttons are always drawn 1 px down from the visible top of the | |
| 210 // window (the true top in restored mode, or the top of the screen in maximized | |
| 211 // mode). | |
| 212 const int kCaptionTopSpacing = 1; | |
| 213 } | |
| 214 | |
| 215 /////////////////////////////////////////////////////////////////////////////// | |
| 216 // DefaultNonClientView, public: | |
| 217 | |
| 218 DefaultNonClientView::DefaultNonClientView( | |
| 219 CustomFrameWindow* container) | |
| 220 : NonClientView(), | |
| 221 client_view_(NULL), | |
| 222 close_button_(new Button), | |
| 223 restore_button_(new Button), | |
| 224 maximize_button_(new Button), | |
| 225 minimize_button_(new Button), | |
| 226 system_menu_button_(new Button), | |
| 227 should_show_minmax_buttons_(false), | |
| 228 container_(container) { | |
| 229 InitClass(); | |
| 230 WindowResources* resources = active_resources_; | |
| 231 | |
| 232 // Close button images will be set in LayoutWindowControls(). | |
| 233 close_button_->SetListener(this, -1); | |
| 234 AddChildView(close_button_); | |
| 235 | |
| 236 restore_button_->SetImage(Button::BS_NORMAL, | |
| 237 resources->GetPartBitmap(FRAME_RESTORE_BUTTON_ICON)); | |
| 238 restore_button_->SetImage(Button::BS_HOT, | |
| 239 resources->GetPartBitmap(FRAME_RESTORE_BUTTON_ICON_H)); | |
| 240 restore_button_->SetImage(Button::BS_PUSHED, | |
| 241 resources->GetPartBitmap(FRAME_RESTORE_BUTTON_ICON_P)); | |
| 242 restore_button_->SetListener(this, -1); | |
| 243 AddChildView(restore_button_); | |
| 244 | |
| 245 maximize_button_->SetImage(Button::BS_NORMAL, | |
| 246 resources->GetPartBitmap(FRAME_MAXIMIZE_BUTTON_ICON)); | |
| 247 maximize_button_->SetImage(Button::BS_HOT, | |
| 248 resources->GetPartBitmap(FRAME_MAXIMIZE_BUTTON_ICON_H)); | |
| 249 maximize_button_->SetImage(Button::BS_PUSHED, | |
| 250 resources->GetPartBitmap(FRAME_MAXIMIZE_BUTTON_ICON_P)); | |
| 251 maximize_button_->SetListener(this, -1); | |
| 252 AddChildView(maximize_button_); | |
| 253 | |
| 254 minimize_button_->SetImage(Button::BS_NORMAL, | |
| 255 resources->GetPartBitmap(FRAME_MINIMIZE_BUTTON_ICON)); | |
| 256 minimize_button_->SetImage(Button::BS_HOT, | |
| 257 resources->GetPartBitmap(FRAME_MINIMIZE_BUTTON_ICON_H)); | |
| 258 minimize_button_->SetImage(Button::BS_PUSHED, | |
| 259 resources->GetPartBitmap(FRAME_MINIMIZE_BUTTON_ICON_P)); | |
| 260 minimize_button_->SetListener(this, -1); | |
| 261 AddChildView(minimize_button_); | |
| 262 | |
| 263 should_show_minmax_buttons_ = container->window_delegate()->CanMaximize(); | |
| 264 | |
| 265 AddChildView(system_menu_button_); | |
| 266 } | |
| 267 | |
| 268 DefaultNonClientView::~DefaultNonClientView() { | |
| 269 } | |
| 270 | |
| 271 /////////////////////////////////////////////////////////////////////////////// | |
| 272 // DefaultNonClientView, CustomFrameWindow::NonClientView implementation: | |
| 273 | |
| 274 gfx::Rect DefaultNonClientView::CalculateClientAreaBounds(int width, | |
| 275 int height) const { | |
| 276 int top_height = NonClientTopBorderHeight(); | |
| 277 int border_thickness = NonClientBorderThickness(); | |
| 278 return gfx::Rect(border_thickness, top_height, | |
| 279 std::max(0, width - (2 * border_thickness)), | |
| 280 std::max(0, height - top_height - border_thickness)); | |
| 281 } | |
| 282 | |
| 283 gfx::Size DefaultNonClientView::CalculateWindowSizeForClientSize( | |
| 284 int width, | |
| 285 int height) const { | |
| 286 int border_thickness = NonClientBorderThickness(); | |
| 287 return gfx::Size(width + (2 * border_thickness), | |
| 288 height + NonClientTopBorderHeight() + border_thickness); | |
| 289 } | |
| 290 | |
| 291 gfx::Point DefaultNonClientView::GetSystemMenuPoint() const { | |
| 292 gfx::Point system_menu_point(FrameBorderThickness(), | |
| 293 NonClientTopBorderHeight() - BottomEdgeThicknessWithinNonClientHeight()); | |
| 294 ConvertPointToScreen(this, &system_menu_point); | |
| 295 return system_menu_point; | |
| 296 } | |
| 297 | |
| 298 int DefaultNonClientView::NonClientHitTest(const gfx::Point& point) { | |
| 299 if (!bounds().Contains(point)) | |
| 300 return HTNOWHERE; | |
| 301 | |
| 302 int frame_component = container_->client_view()->NonClientHitTest(point); | |
| 303 if (frame_component != HTNOWHERE) | |
| 304 return frame_component; | |
| 305 | |
| 306 // Then see if the point is within any of the window controls. | |
| 307 if (close_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains(point)) | |
| 308 return HTCLOSE; | |
| 309 if (restore_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains( | |
| 310 point)) | |
| 311 return HTMAXBUTTON; | |
| 312 if (maximize_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains( | |
| 313 point)) | |
| 314 return HTMAXBUTTON; | |
| 315 if (minimize_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains( | |
| 316 point)) | |
| 317 return HTMINBUTTON; | |
| 318 if (system_menu_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains( | |
| 319 point)) | |
| 320 return HTSYSMENU; | |
| 321 | |
| 322 int window_component = GetHTComponentForFrame(point, FrameBorderThickness(), | |
| 323 NonClientBorderThickness(), kResizeAreaCornerSize, kResizeAreaCornerSize, | |
| 324 container_->window_delegate()->CanResize()); | |
| 325 // Fall back to the caption if no other component matches. | |
| 326 return (window_component == HTNOWHERE) ? HTCAPTION : window_component; | |
| 327 } | |
| 328 | |
| 329 void DefaultNonClientView::GetWindowMask(const gfx::Size& size, | |
| 330 gfx::Path* window_mask) { | |
| 331 DCHECK(window_mask); | |
| 332 | |
| 333 // Redefine the window visible region for the new size. | |
| 334 window_mask->moveTo(0, 3); | |
| 335 window_mask->lineTo(1, 2); | |
| 336 window_mask->lineTo(1, 1); | |
| 337 window_mask->lineTo(2, 1); | |
| 338 window_mask->lineTo(3, 0); | |
| 339 | |
| 340 window_mask->lineTo(SkIntToScalar(size.width() - 3), 0); | |
| 341 window_mask->lineTo(SkIntToScalar(size.width() - 2), 1); | |
| 342 window_mask->lineTo(SkIntToScalar(size.width() - 1), 1); | |
| 343 window_mask->lineTo(SkIntToScalar(size.width() - 1), 2); | |
| 344 window_mask->lineTo(SkIntToScalar(size.width()), 3); | |
| 345 | |
| 346 window_mask->lineTo(SkIntToScalar(size.width()), | |
| 347 SkIntToScalar(size.height())); | |
| 348 window_mask->lineTo(0, SkIntToScalar(size.height())); | |
| 349 window_mask->close(); | |
| 350 } | |
| 351 | |
| 352 void DefaultNonClientView::EnableClose(bool enable) { | |
| 353 close_button_->SetEnabled(enable); | |
| 354 } | |
| 355 | |
| 356 void DefaultNonClientView::ResetWindowControls() { | |
| 357 restore_button_->SetState(Button::BS_NORMAL); | |
| 358 minimize_button_->SetState(Button::BS_NORMAL); | |
| 359 maximize_button_->SetState(Button::BS_NORMAL); | |
| 360 // The close button isn't affected by this constraint. | |
| 361 } | |
| 362 | |
| 363 /////////////////////////////////////////////////////////////////////////////// | |
| 364 // DefaultNonClientView, View overrides: | |
| 365 | |
| 366 void DefaultNonClientView::Paint(ChromeCanvas* canvas) { | |
| 367 if (container_->IsMaximized()) | |
| 368 PaintMaximizedFrameBorder(canvas); | |
| 369 else | |
| 370 PaintRestoredFrameBorder(canvas); | |
| 371 PaintTitleBar(canvas); | |
| 372 if (!container_->IsMaximized()) | |
| 373 PaintRestoredClientEdge(canvas); | |
| 374 } | |
| 375 | |
| 376 void DefaultNonClientView::Layout() { | |
| 377 LayoutWindowControls(); | |
| 378 LayoutTitleBar(); | |
| 379 LayoutClientView(); | |
| 380 } | |
| 381 | |
| 382 gfx::Size DefaultNonClientView::GetPreferredSize() { | |
| 383 gfx::Size pref = client_view_->GetPreferredSize(); | |
| 384 DCHECK(pref.width() > 0 && pref.height() > 0); | |
| 385 return CalculateWindowSizeForClientSize(pref.width(), pref.height()); | |
| 386 } | |
| 387 | |
| 388 void DefaultNonClientView::ViewHierarchyChanged(bool is_add, | |
| 389 View* parent, | |
| 390 View* child) { | |
| 391 // Add our Client View as we are added to the Widget so that if we are | |
| 392 // subsequently resized all the parent-child relationships are established. | |
| 393 if (is_add && GetWidget() && child == this) | |
| 394 AddChildView(container_->client_view()); | |
| 395 } | |
| 396 | |
| 397 /////////////////////////////////////////////////////////////////////////////// | |
| 398 // DefaultNonClientView, BaseButton::ButtonListener implementation: | |
| 399 | |
| 400 void DefaultNonClientView::ButtonPressed(BaseButton* sender) { | |
| 401 if (sender == close_button_) | |
| 402 container_->ExecuteSystemMenuCommand(SC_CLOSE); | |
| 403 else if (sender == minimize_button_) | |
| 404 container_->ExecuteSystemMenuCommand(SC_MINIMIZE); | |
| 405 else if (sender == maximize_button_) | |
| 406 container_->ExecuteSystemMenuCommand(SC_MAXIMIZE); | |
| 407 else if (sender == restore_button_) | |
| 408 container_->ExecuteSystemMenuCommand(SC_RESTORE); | |
| 409 } | |
| 410 | |
| 411 /////////////////////////////////////////////////////////////////////////////// | |
| 412 // DefaultNonClientView, private: | |
| 413 | |
| 414 int DefaultNonClientView::FrameBorderThickness() const { | |
| 415 return container_->IsMaximized() ? | |
| 416 GetSystemMetrics(SM_CXSIZEFRAME) : kFrameBorderThickness; | |
| 417 } | |
| 418 | |
| 419 int DefaultNonClientView::NonClientBorderThickness() const { | |
| 420 // In maximized mode, we don't show a client edge. | |
| 421 return FrameBorderThickness() + | |
| 422 (container_->IsMaximized() ? 0 : kClientEdgeThickness); | |
| 423 } | |
| 424 | |
| 425 int DefaultNonClientView::NonClientTopBorderHeight() const { | |
| 426 int title_top_spacing, title_thickness; | |
| 427 return TitleCoordinates(&title_top_spacing, &title_thickness); | |
| 428 } | |
| 429 | |
| 430 int DefaultNonClientView::BottomEdgeThicknessWithinNonClientHeight() const { | |
| 431 return kFrameShadowThickness + | |
| 432 (container_->IsMaximized() ? 0 : kClientEdgeThickness); | |
| 433 } | |
| 434 | |
| 435 int DefaultNonClientView::TitleCoordinates(int* title_top_spacing, | |
| 436 int* title_thickness) const { | |
| 437 int frame_thickness = FrameBorderThickness(); | |
| 438 int min_titlebar_height = kTitlebarMinimumHeight + frame_thickness; | |
| 439 *title_top_spacing = frame_thickness + kTitleTopSpacing; | |
| 440 // The bottom spacing should be the same apparent height as the top spacing. | |
| 441 // Because the actual top spacing height varies based on the system border | |
| 442 // thickness, we calculate this based on the restored top spacing and then | |
| 443 // adjust for maximized mode. We also don't include the frame shadow here, | |
| 444 // since while it's part of the bottom spacing it will be added in at the end. | |
| 445 int title_bottom_spacing = | |
| 446 kFrameBorderThickness + kTitleTopSpacing - kFrameShadowThickness; | |
| 447 if (container_->IsMaximized()) { | |
| 448 // When we maximize, the top border appears to be chopped off; shift the | |
| 449 // title down to stay centered within the remaining space. | |
| 450 int title_adjust = (kFrameBorderThickness / 2); | |
| 451 *title_top_spacing += title_adjust; | |
| 452 title_bottom_spacing -= title_adjust; | |
| 453 } | |
| 454 *title_thickness = std::max(title_font_.height(), | |
| 455 min_titlebar_height - *title_top_spacing - title_bottom_spacing); | |
| 456 return *title_top_spacing + *title_thickness + title_bottom_spacing + | |
| 457 BottomEdgeThicknessWithinNonClientHeight(); | |
| 458 } | |
| 459 | |
| 460 void DefaultNonClientView::PaintRestoredFrameBorder(ChromeCanvas* canvas) { | |
| 461 SkBitmap* top_left_corner = resources()->GetPartBitmap(FRAME_TOP_LEFT_CORNER); | |
| 462 SkBitmap* top_right_corner = | |
| 463 resources()->GetPartBitmap(FRAME_TOP_RIGHT_CORNER); | |
| 464 SkBitmap* top_edge = resources()->GetPartBitmap(FRAME_TOP_EDGE); | |
| 465 SkBitmap* right_edge = resources()->GetPartBitmap(FRAME_RIGHT_EDGE); | |
| 466 SkBitmap* left_edge = resources()->GetPartBitmap(FRAME_LEFT_EDGE); | |
| 467 SkBitmap* bottom_left_corner = | |
| 468 resources()->GetPartBitmap(FRAME_BOTTOM_LEFT_CORNER); | |
| 469 SkBitmap* bottom_right_corner = | |
| 470 resources()->GetPartBitmap(FRAME_BOTTOM_RIGHT_CORNER); | |
| 471 SkBitmap* bottom_edge = resources()->GetPartBitmap(FRAME_BOTTOM_EDGE); | |
| 472 | |
| 473 // Top. | |
| 474 canvas->DrawBitmapInt(*top_left_corner, 0, 0); | |
| 475 canvas->TileImageInt(*top_edge, top_left_corner->width(), 0, | |
| 476 width() - top_right_corner->width(), top_edge->height()); | |
| 477 canvas->DrawBitmapInt(*top_right_corner, | |
| 478 width() - top_right_corner->width(), 0); | |
| 479 | |
| 480 // Right. | |
| 481 canvas->TileImageInt(*right_edge, width() - right_edge->width(), | |
| 482 top_right_corner->height(), right_edge->width(), | |
| 483 height() - top_right_corner->height() - | |
| 484 bottom_right_corner->height()); | |
| 485 | |
| 486 // Bottom. | |
| 487 canvas->DrawBitmapInt(*bottom_right_corner, | |
| 488 width() - bottom_right_corner->width(), | |
| 489 height() - bottom_right_corner->height()); | |
| 490 canvas->TileImageInt(*bottom_edge, bottom_left_corner->width(), | |
| 491 height() - bottom_edge->height(), | |
| 492 width() - bottom_left_corner->width() - | |
| 493 bottom_right_corner->width(), | |
| 494 bottom_edge->height()); | |
| 495 canvas->DrawBitmapInt(*bottom_left_corner, 0, | |
| 496 height() - bottom_left_corner->height()); | |
| 497 | |
| 498 // Left. | |
| 499 canvas->TileImageInt(*left_edge, 0, top_left_corner->height(), | |
| 500 left_edge->width(), | |
| 501 height() - top_left_corner->height() - bottom_left_corner->height()); | |
| 502 } | |
| 503 | |
| 504 void DefaultNonClientView::PaintMaximizedFrameBorder( | |
| 505 ChromeCanvas* canvas) { | |
| 506 SkBitmap* top_edge = resources()->GetPartBitmap(FRAME_TOP_EDGE); | |
| 507 canvas->TileImageInt(*top_edge, 0, FrameBorderThickness(), width(), | |
| 508 top_edge->height()); | |
| 509 | |
| 510 // The bottom of the titlebar actually comes from the top of the Client Edge | |
| 511 // graphic, with the actual client edge clipped off the bottom. | |
| 512 SkBitmap* titlebar_bottom = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP); | |
| 513 int edge_height = titlebar_bottom->height() - kClientEdgeThickness; | |
| 514 canvas->TileImageInt(*titlebar_bottom, 0, | |
| 515 container_->client_view()->y() - edge_height, width(), edge_height); | |
| 516 } | |
| 517 | |
| 518 void DefaultNonClientView::PaintTitleBar(ChromeCanvas* canvas) { | |
| 519 WindowDelegate* d = container_->window_delegate(); | |
| 520 | |
| 521 // It seems like in some conditions we can be asked to paint after the window | |
| 522 // that contains us is WM_DESTROYed. At this point, our delegate is NULL. The | |
| 523 // correct long term fix may be to shut down the RootView in WM_DESTROY. | |
| 524 if (!d) | |
| 525 return; | |
| 526 | |
| 527 canvas->DrawStringInt(d->GetWindowTitle(), title_font_, SK_ColorWHITE, | |
| 528 MirroredLeftPointForRect(title_bounds_), title_bounds_.y(), | |
| 529 title_bounds_.width(), title_bounds_.height()); | |
| 530 } | |
| 531 | |
| 532 void DefaultNonClientView::PaintRestoredClientEdge(ChromeCanvas* canvas) { | |
| 533 gfx::Rect client_area_bounds = container_->client_view()->bounds(); | |
| 534 int client_area_top = client_area_bounds.y(); | |
| 535 | |
| 536 SkBitmap* top_left = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP_LEFT); | |
| 537 SkBitmap* top = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP); | |
| 538 SkBitmap* top_right = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP_RIGHT); | |
| 539 SkBitmap* right = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_RIGHT); | |
| 540 SkBitmap* bottom_right = | |
| 541 resources()->GetPartBitmap(FRAME_CLIENT_EDGE_BOTTOM_RIGHT); | |
| 542 SkBitmap* bottom = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_BOTTOM); | |
| 543 SkBitmap* bottom_left = | |
| 544 resources()->GetPartBitmap(FRAME_CLIENT_EDGE_BOTTOM_LEFT); | |
| 545 SkBitmap* left = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_LEFT); | |
| 546 | |
| 547 // Top. | |
| 548 // This next calculation is necessary because the top center bitmap is shorter | |
| 549 // than the top left and right bitmaps. We need their top edges to line up, | |
| 550 // and we need the left and right edges to start below the corners' bottoms. | |
| 551 int top_edge_y = client_area_top - top->height(); | |
| 552 client_area_top = top_edge_y + top_left->height(); | |
| 553 canvas->DrawBitmapInt(*top_left, client_area_bounds.x() - top_left->width(), | |
| 554 top_edge_y); | |
| 555 canvas->TileImageInt(*top, client_area_bounds.x(), top_edge_y, | |
| 556 client_area_bounds.width(), top->height()); | |
| 557 canvas->DrawBitmapInt(*top_right, client_area_bounds.right(), top_edge_y); | |
| 558 | |
| 559 // Right. | |
| 560 int client_area_bottom = | |
| 561 std::max(client_area_top, client_area_bounds.bottom()); | |
| 562 int client_area_height = client_area_bottom - client_area_top; | |
| 563 canvas->TileImageInt(*right, client_area_bounds.right(), client_area_top, | |
| 564 right->width(), client_area_height); | |
| 565 | |
| 566 // Bottom. | |
| 567 canvas->DrawBitmapInt(*bottom_right, client_area_bounds.right(), | |
| 568 client_area_bottom); | |
| 569 canvas->TileImageInt(*bottom, client_area_bounds.x(), client_area_bottom, | |
| 570 client_area_bounds.width(), bottom_right->height()); | |
| 571 canvas->DrawBitmapInt(*bottom_left, | |
| 572 client_area_bounds.x() - bottom_left->width(), client_area_bottom); | |
| 573 | |
| 574 // Left. | |
| 575 canvas->TileImageInt(*left, client_area_bounds.x() - left->width(), | |
| 576 client_area_top, left->width(), client_area_height); | |
| 577 } | |
| 578 | |
| 579 void DefaultNonClientView::LayoutWindowControls() { | |
| 580 close_button_->SetImageAlignment(Button::ALIGN_LEFT, Button::ALIGN_BOTTOM); | |
| 581 // Maximized buttons start at window top so that even if their images aren't | |
| 582 // drawn flush with the screen edge, they still obey Fitts' Law. | |
| 583 bool is_maximized = container_->IsMaximized(); | |
| 584 int frame_thickness = FrameBorderThickness(); | |
| 585 int caption_y = is_maximized ? frame_thickness : kCaptionTopSpacing; | |
| 586 int top_extra_height = is_maximized ? kCaptionTopSpacing : 0; | |
| 587 // There should always be the same number of non-shadow pixels visible to the | |
| 588 // side of the caption buttons. In maximized mode we extend the rightmost | |
| 589 // button to the screen corner to obey Fitts' Law. | |
| 590 int right_extra_width = is_maximized ? | |
| 591 (kFrameBorderThickness - kFrameShadowThickness) : 0; | |
| 592 int right_spacing = is_maximized ? | |
| 593 (GetSystemMetrics(SM_CXSIZEFRAME) + right_extra_width) : frame_thickness; | |
| 594 gfx::Size close_button_size = close_button_->GetPreferredSize(); | |
| 595 close_button_->SetBounds(width() - close_button_size.width() - right_spacing, | |
| 596 caption_y, | |
| 597 close_button_size.width() + right_extra_width, | |
| 598 close_button_size.height() + top_extra_height); | |
| 599 | |
| 600 // When the window is restored, we show a maximized button; otherwise, we show | |
| 601 // a restore button. | |
| 602 bool is_restored = !is_maximized && !container_->IsMinimized(); | |
| 603 views::Button* invisible_button = is_restored ? | |
| 604 restore_button_ : maximize_button_; | |
| 605 invisible_button->SetVisible(false); | |
| 606 | |
| 607 views::Button* visible_button = is_restored ? | |
| 608 maximize_button_ : restore_button_; | |
| 609 FramePartBitmap normal_part, hot_part, pushed_part; | |
| 610 if (should_show_minmax_buttons_) { | |
| 611 visible_button->SetVisible(true); | |
| 612 visible_button->SetImageAlignment(Button::ALIGN_LEFT, Button::ALIGN_BOTTOM); | |
| 613 gfx::Size visible_button_size = visible_button->GetPreferredSize(); | |
| 614 visible_button->SetBounds(close_button_->x() - visible_button_size.width(), | |
| 615 caption_y, visible_button_size.width(), | |
| 616 visible_button_size.height() + top_extra_height); | |
| 617 | |
| 618 minimize_button_->SetVisible(true); | |
| 619 minimize_button_->SetImageAlignment(Button::ALIGN_LEFT, | |
| 620 Button::ALIGN_BOTTOM); | |
| 621 gfx::Size minimize_button_size = minimize_button_->GetPreferredSize(); | |
| 622 minimize_button_->SetBounds( | |
| 623 visible_button->x() - minimize_button_size.width(), caption_y, | |
| 624 minimize_button_size.width(), | |
| 625 minimize_button_size.height() + top_extra_height); | |
| 626 | |
| 627 normal_part = FRAME_CLOSE_BUTTON_ICON; | |
| 628 hot_part = FRAME_CLOSE_BUTTON_ICON_H; | |
| 629 pushed_part = FRAME_CLOSE_BUTTON_ICON_P; | |
| 630 } else { | |
| 631 visible_button->SetVisible(false); | |
| 632 minimize_button_->SetVisible(false); | |
| 633 | |
| 634 normal_part = FRAME_CLOSE_BUTTON_ICON_SA; | |
| 635 hot_part = FRAME_CLOSE_BUTTON_ICON_SA_H; | |
| 636 pushed_part = FRAME_CLOSE_BUTTON_ICON_SA_P; | |
| 637 } | |
| 638 | |
| 639 close_button_->SetImage(Button::BS_NORMAL, | |
| 640 active_resources_->GetPartBitmap(normal_part)); | |
| 641 close_button_->SetImage(Button::BS_HOT, | |
| 642 active_resources_->GetPartBitmap(hot_part)); | |
| 643 close_button_->SetImage(Button::BS_PUSHED, | |
| 644 active_resources_->GetPartBitmap(pushed_part)); | |
| 645 } | |
| 646 | |
| 647 void DefaultNonClientView::LayoutTitleBar() { | |
| 648 // Always lay out the icon, even when it's not present, so we can lay out the | |
| 649 // window title based on its position. | |
| 650 int frame_thickness = FrameBorderThickness(); | |
| 651 int icon_x = frame_thickness + kIconLeftSpacing; | |
| 652 | |
| 653 // The usable height of the titlebar area is the total height minus the top | |
| 654 // resize border and any edge area we draw at its bottom. | |
| 655 int title_top_spacing, title_thickness; | |
| 656 int top_height = TitleCoordinates(&title_top_spacing, &title_thickness); | |
| 657 int available_height = top_height - frame_thickness - | |
| 658 BottomEdgeThicknessWithinNonClientHeight(); | |
| 659 | |
| 660 // The icon takes up a constant fraction of the available height, down to a | |
| 661 // minimum size, and is always an even number of pixels on a side (presumably | |
| 662 // to make scaled icons look better). It's centered within the usable height. | |
| 663 int icon_size = std::max((available_height * kIconHeightFractionNumerator / | |
| 664 kIconHeightFractionDenominator) / 2 * 2, kIconMinimumSize); | |
| 665 int icon_y = ((available_height - icon_size) / 2) + frame_thickness; | |
| 666 | |
| 667 // Hack: Our frame border has a different "3D look" than Windows'. Theirs has | |
| 668 // a more complex gradient on the top that they push their icon/title below; | |
| 669 // then the maximized window cuts this off and the icon/title are centered in | |
| 670 // the remaining space. Because the apparent shape of our border is simpler, | |
| 671 // using the same positioning makes things look slightly uncentered with | |
| 672 // restored windows, so we come up to compensate. | |
| 673 if (!container_->IsMaximized()) | |
| 674 icon_y -= kIconRestoredAdjust; | |
| 675 | |
| 676 views::WindowDelegate* d = container_->window_delegate(); | |
| 677 if (!d->ShouldShowWindowIcon()) | |
| 678 icon_size = 0; | |
| 679 system_menu_button_->SetBounds(icon_x, icon_y, icon_size, icon_size); | |
| 680 | |
| 681 // Size the title. | |
| 682 int icon_right = icon_x + icon_size; | |
| 683 int title_x = | |
| 684 icon_right + (d->ShouldShowWindowIcon() ? kIconTitleSpacing : 0); | |
| 685 int title_right = (should_show_minmax_buttons_ ? | |
| 686 minimize_button_->x() : close_button_->x()) - kTitleCaptionSpacing; | |
| 687 title_bounds_.SetRect(title_x, | |
| 688 title_top_spacing + ((title_thickness - title_font_.height()) / 2), | |
| 689 std::max(0, title_right - title_x), title_font_.height()); | |
| 690 } | |
| 691 | |
| 692 void DefaultNonClientView::LayoutClientView() { | |
| 693 container_->client_view()->SetBounds(CalculateClientAreaBounds(width(), | |
| 694 height())); | |
| 695 } | |
| 696 | |
| 697 // static | |
| 698 void DefaultNonClientView::InitClass() { | |
| 699 static bool initialized = false; | |
| 700 if (!initialized) { | |
| 701 active_resources_ = new ActiveWindowResources; | |
| 702 inactive_resources_ = new InactiveWindowResources; | |
| 703 | |
| 704 title_font_ = win_util::GetWindowTitleFont(); | |
| 705 | |
| 706 initialized = true; | |
| 707 } | |
| 708 } | |
| 709 | |
| 710 } // namespace views | |
| OLD | NEW |