| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2006-2008 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/views/frame/opaque_non_client_view.h" | |
| 6 | |
| 7 #include "chrome/browser/views/frame/browser_view.h" | |
| 8 #include "chrome/browser/views/tabs/tab_strip.h" | |
| 9 #include "chrome/common/gfx/chrome_font.h" | |
| 10 #include "chrome/common/gfx/path.h" | |
| 11 #include "chrome/common/l10n_util.h" | |
| 12 #include "chrome/common/resource_bundle.h" | |
| 13 #include "chrome/common/win_util.h" | |
| 14 #include "chrome/views/root_view.h" | |
| 15 #include "chrome/views/window_resources.h" | |
| 16 #include "grit/chromium_strings.h" | |
| 17 #include "grit/generated_resources.h" | |
| 18 #include "grit/theme_resources.h" | |
| 19 | |
| 20 // An enumeration of bitmap resources used by this window. | |
| 21 enum { | |
| 22 // Window Controls. | |
| 23 FRAME_CLOSE_BUTTON_ICON, | |
| 24 FRAME_CLOSE_BUTTON_ICON_H, | |
| 25 FRAME_CLOSE_BUTTON_ICON_P, | |
| 26 FRAME_CLOSE_BUTTON_ICON_SA, | |
| 27 FRAME_CLOSE_BUTTON_ICON_SA_H, | |
| 28 FRAME_CLOSE_BUTTON_ICON_SA_P, | |
| 29 FRAME_RESTORE_BUTTON_ICON, | |
| 30 FRAME_RESTORE_BUTTON_ICON_H, | |
| 31 FRAME_RESTORE_BUTTON_ICON_P, | |
| 32 FRAME_MAXIMIZE_BUTTON_ICON, | |
| 33 FRAME_MAXIMIZE_BUTTON_ICON_H, | |
| 34 FRAME_MAXIMIZE_BUTTON_ICON_P, | |
| 35 FRAME_MINIMIZE_BUTTON_ICON, | |
| 36 FRAME_MINIMIZE_BUTTON_ICON_H, | |
| 37 FRAME_MINIMIZE_BUTTON_ICON_P, | |
| 38 | |
| 39 // Window Frame Border. | |
| 40 FRAME_BOTTOM_EDGE, | |
| 41 FRAME_BOTTOM_LEFT_CORNER, | |
| 42 FRAME_BOTTOM_RIGHT_CORNER, | |
| 43 FRAME_LEFT_EDGE, | |
| 44 FRAME_RIGHT_EDGE, | |
| 45 FRAME_TOP_EDGE, | |
| 46 FRAME_TOP_LEFT_CORNER, | |
| 47 FRAME_TOP_RIGHT_CORNER, | |
| 48 | |
| 49 // Client Edge Border. | |
| 50 FRAME_CLIENT_EDGE_TOP_LEFT, | |
| 51 FRAME_CLIENT_EDGE_TOP, | |
| 52 FRAME_CLIENT_EDGE_TOP_RIGHT, | |
| 53 FRAME_CLIENT_EDGE_RIGHT, | |
| 54 FRAME_CLIENT_EDGE_BOTTOM_RIGHT, | |
| 55 FRAME_CLIENT_EDGE_BOTTOM, | |
| 56 FRAME_CLIENT_EDGE_BOTTOM_LEFT, | |
| 57 FRAME_CLIENT_EDGE_LEFT, | |
| 58 | |
| 59 // No-toolbar client edge. | |
| 60 FRAME_NO_TOOLBAR_TOP_LEFT, | |
| 61 FRAME_NO_TOOLBAR_TOP_CENTER, | |
| 62 FRAME_NO_TOOLBAR_TOP_RIGHT, | |
| 63 | |
| 64 FRAME_PART_BITMAP_COUNT // Must be last. | |
| 65 }; | |
| 66 | |
| 67 class ActiveWindowResources : public views::WindowResources { | |
| 68 public: | |
| 69 ActiveWindowResources() { | |
| 70 InitClass(); | |
| 71 } | |
| 72 virtual ~ActiveWindowResources() { } | |
| 73 | |
| 74 // WindowResources implementation: | |
| 75 virtual SkBitmap* GetPartBitmap(views::FramePartBitmap part) const { | |
| 76 return standard_frame_bitmaps_[part]; | |
| 77 } | |
| 78 | |
| 79 private: | |
| 80 static void InitClass() { | |
| 81 static bool initialized = false; | |
| 82 if (!initialized) { | |
| 83 static const int kFramePartBitmapIds[] = { | |
| 84 IDR_CLOSE, IDR_CLOSE_H, IDR_CLOSE_P, | |
| 85 IDR_CLOSE_SA, IDR_CLOSE_SA_H, IDR_CLOSE_SA_P, | |
| 86 IDR_RESTORE, IDR_RESTORE_H, IDR_RESTORE_P, | |
| 87 IDR_MAXIMIZE, IDR_MAXIMIZE_H, IDR_MAXIMIZE_P, | |
| 88 IDR_MINIMIZE, IDR_MINIMIZE_H, IDR_MINIMIZE_P, | |
| 89 IDR_WINDOW_BOTTOM_CENTER, IDR_WINDOW_BOTTOM_LEFT_CORNER, | |
| 90 IDR_WINDOW_BOTTOM_RIGHT_CORNER, IDR_WINDOW_LEFT_SIDE, | |
| 91 IDR_WINDOW_RIGHT_SIDE, IDR_WINDOW_TOP_CENTER, | |
| 92 IDR_WINDOW_TOP_LEFT_CORNER, IDR_WINDOW_TOP_RIGHT_CORNER, | |
| 93 IDR_CONTENT_TOP_LEFT_CORNER, IDR_CONTENT_TOP_CENTER, | |
| 94 IDR_CONTENT_TOP_RIGHT_CORNER, IDR_CONTENT_RIGHT_SIDE, | |
| 95 IDR_CONTENT_BOTTOM_RIGHT_CORNER, IDR_CONTENT_BOTTOM_CENTER, | |
| 96 IDR_CONTENT_BOTTOM_LEFT_CORNER, IDR_CONTENT_LEFT_SIDE, | |
| 97 IDR_APP_TOP_LEFT, IDR_APP_TOP_CENTER, IDR_APP_TOP_RIGHT, | |
| 98 }; | |
| 99 | |
| 100 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 101 for (int i = 0; i < FRAME_PART_BITMAP_COUNT; ++i) | |
| 102 standard_frame_bitmaps_[i] = rb.GetBitmapNamed(kFramePartBitmapIds[i]); | |
| 103 initialized = true; | |
| 104 } | |
| 105 } | |
| 106 | |
| 107 static SkBitmap* standard_frame_bitmaps_[FRAME_PART_BITMAP_COUNT]; | |
| 108 | |
| 109 DISALLOW_EVIL_CONSTRUCTORS(ActiveWindowResources); | |
| 110 }; | |
| 111 | |
| 112 class InactiveWindowResources : public views::WindowResources { | |
| 113 public: | |
| 114 InactiveWindowResources() { | |
| 115 InitClass(); | |
| 116 } | |
| 117 virtual ~InactiveWindowResources() { } | |
| 118 | |
| 119 // WindowResources implementation: | |
| 120 virtual SkBitmap* GetPartBitmap(views::FramePartBitmap part) const { | |
| 121 return standard_frame_bitmaps_[part]; | |
| 122 } | |
| 123 | |
| 124 private: | |
| 125 static void InitClass() { | |
| 126 static bool initialized = false; | |
| 127 if (!initialized) { | |
| 128 static const int kFramePartBitmapIds[] = { | |
| 129 IDR_CLOSE, IDR_CLOSE_H, IDR_CLOSE_P, | |
| 130 IDR_CLOSE_SA, IDR_CLOSE_SA_H, IDR_CLOSE_SA_P, | |
| 131 IDR_RESTORE, IDR_RESTORE_H, IDR_RESTORE_P, | |
| 132 IDR_MAXIMIZE, IDR_MAXIMIZE_H, IDR_MAXIMIZE_P, | |
| 133 IDR_MINIMIZE, IDR_MINIMIZE_H, IDR_MINIMIZE_P, | |
| 134 IDR_DEWINDOW_BOTTOM_CENTER, IDR_DEWINDOW_BOTTOM_LEFT_CORNER, | |
| 135 IDR_DEWINDOW_BOTTOM_RIGHT_CORNER, IDR_DEWINDOW_LEFT_SIDE, | |
| 136 IDR_DEWINDOW_RIGHT_SIDE, IDR_DEWINDOW_TOP_CENTER, | |
| 137 IDR_DEWINDOW_TOP_LEFT_CORNER, IDR_DEWINDOW_TOP_RIGHT_CORNER, | |
| 138 IDR_CONTENT_TOP_LEFT_CORNER, IDR_CONTENT_TOP_CENTER, | |
| 139 IDR_CONTENT_TOP_RIGHT_CORNER, IDR_CONTENT_RIGHT_SIDE, | |
| 140 IDR_CONTENT_BOTTOM_RIGHT_CORNER, IDR_CONTENT_BOTTOM_CENTER, | |
| 141 IDR_CONTENT_BOTTOM_LEFT_CORNER, IDR_CONTENT_LEFT_SIDE, | |
| 142 IDR_APP_TOP_LEFT, IDR_APP_TOP_CENTER, IDR_APP_TOP_RIGHT, | |
| 143 }; | |
| 144 | |
| 145 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 146 for (int i = 0; i < FRAME_PART_BITMAP_COUNT; ++i) | |
| 147 standard_frame_bitmaps_[i] = rb.GetBitmapNamed(kFramePartBitmapIds[i]); | |
| 148 initialized = true; | |
| 149 } | |
| 150 } | |
| 151 | |
| 152 static SkBitmap* standard_frame_bitmaps_[FRAME_PART_BITMAP_COUNT]; | |
| 153 | |
| 154 DISALLOW_EVIL_CONSTRUCTORS(InactiveWindowResources); | |
| 155 }; | |
| 156 | |
| 157 class OTRActiveWindowResources : public views::WindowResources { | |
| 158 public: | |
| 159 OTRActiveWindowResources() { | |
| 160 InitClass(); | |
| 161 } | |
| 162 virtual ~OTRActiveWindowResources() { } | |
| 163 | |
| 164 // WindowResources implementation: | |
| 165 virtual SkBitmap* GetPartBitmap(views::FramePartBitmap part) const { | |
| 166 return standard_frame_bitmaps_[part]; | |
| 167 } | |
| 168 | |
| 169 private: | |
| 170 static void InitClass() { | |
| 171 static bool initialized = false; | |
| 172 if (!initialized) { | |
| 173 static const int kFramePartBitmapIds[] = { | |
| 174 IDR_CLOSE, IDR_CLOSE_H, IDR_CLOSE_P, | |
| 175 IDR_CLOSE_SA, IDR_CLOSE_SA_H, IDR_CLOSE_SA_P, | |
| 176 IDR_RESTORE, IDR_RESTORE_H, IDR_RESTORE_P, | |
| 177 IDR_MAXIMIZE, IDR_MAXIMIZE_H, IDR_MAXIMIZE_P, | |
| 178 IDR_MINIMIZE, IDR_MINIMIZE_H, IDR_MINIMIZE_P, | |
| 179 IDR_WINDOW_BOTTOM_CENTER_OTR, IDR_WINDOW_BOTTOM_LEFT_CORNER_OTR, | |
| 180 IDR_WINDOW_BOTTOM_RIGHT_CORNER_OTR, IDR_WINDOW_LEFT_SIDE_OTR, | |
| 181 IDR_WINDOW_RIGHT_SIDE_OTR, IDR_WINDOW_TOP_CENTER_OTR, | |
| 182 IDR_WINDOW_TOP_LEFT_CORNER_OTR, IDR_WINDOW_TOP_RIGHT_CORNER_OTR, | |
| 183 IDR_CONTENT_TOP_LEFT_CORNER, IDR_CONTENT_TOP_CENTER, | |
| 184 IDR_CONTENT_TOP_RIGHT_CORNER, IDR_CONTENT_RIGHT_SIDE, | |
| 185 IDR_CONTENT_BOTTOM_RIGHT_CORNER, IDR_CONTENT_BOTTOM_CENTER, | |
| 186 IDR_CONTENT_BOTTOM_LEFT_CORNER, IDR_CONTENT_LEFT_SIDE, | |
| 187 IDR_APP_TOP_LEFT, IDR_APP_TOP_CENTER, IDR_APP_TOP_RIGHT, | |
| 188 }; | |
| 189 | |
| 190 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 191 for (int i = 0; i < FRAME_PART_BITMAP_COUNT; ++i) | |
| 192 standard_frame_bitmaps_[i] = rb.GetBitmapNamed(kFramePartBitmapIds[i]); | |
| 193 initialized = true; | |
| 194 } | |
| 195 } | |
| 196 | |
| 197 static SkBitmap* standard_frame_bitmaps_[FRAME_PART_BITMAP_COUNT]; | |
| 198 | |
| 199 DISALLOW_EVIL_CONSTRUCTORS(OTRActiveWindowResources); | |
| 200 }; | |
| 201 | |
| 202 class OTRInactiveWindowResources : public views::WindowResources { | |
| 203 public: | |
| 204 OTRInactiveWindowResources() { | |
| 205 InitClass(); | |
| 206 } | |
| 207 virtual ~OTRInactiveWindowResources() { } | |
| 208 | |
| 209 // WindowResources implementation: | |
| 210 virtual SkBitmap* GetPartBitmap(views::FramePartBitmap part) const { | |
| 211 return standard_frame_bitmaps_[part]; | |
| 212 } | |
| 213 | |
| 214 private: | |
| 215 static void InitClass() { | |
| 216 static bool initialized = false; | |
| 217 if (!initialized) { | |
| 218 static const int kFramePartBitmapIds[] = { | |
| 219 IDR_CLOSE, IDR_CLOSE_H, IDR_CLOSE_P, | |
| 220 IDR_CLOSE_SA, IDR_CLOSE_SA_H, IDR_CLOSE_SA_P, | |
| 221 IDR_RESTORE, IDR_RESTORE_H, IDR_RESTORE_P, | |
| 222 IDR_MAXIMIZE, IDR_MAXIMIZE_H, IDR_MAXIMIZE_P, | |
| 223 IDR_MINIMIZE, IDR_MINIMIZE_H, IDR_MINIMIZE_P, | |
| 224 IDR_DEWINDOW_BOTTOM_CENTER_OTR, IDR_DEWINDOW_BOTTOM_LEFT_CORNER_OTR, | |
| 225 IDR_DEWINDOW_BOTTOM_RIGHT_CORNER_OTR, IDR_DEWINDOW_LEFT_SIDE_OTR, | |
| 226 IDR_DEWINDOW_RIGHT_SIDE_OTR, IDR_DEWINDOW_TOP_CENTER_OTR, | |
| 227 IDR_DEWINDOW_TOP_LEFT_CORNER_OTR, | |
| 228 IDR_DEWINDOW_TOP_RIGHT_CORNER_OTR, | |
| 229 IDR_CONTENT_TOP_LEFT_CORNER, IDR_CONTENT_TOP_CENTER, | |
| 230 IDR_CONTENT_TOP_RIGHT_CORNER, IDR_CONTENT_RIGHT_SIDE, | |
| 231 IDR_CONTENT_BOTTOM_RIGHT_CORNER, IDR_CONTENT_BOTTOM_CENTER, | |
| 232 IDR_CONTENT_BOTTOM_LEFT_CORNER, IDR_CONTENT_LEFT_SIDE, | |
| 233 IDR_APP_TOP_LEFT, IDR_APP_TOP_CENTER, IDR_APP_TOP_RIGHT, | |
| 234 }; | |
| 235 | |
| 236 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 237 for (int i = 0; i < FRAME_PART_BITMAP_COUNT; ++i) | |
| 238 standard_frame_bitmaps_[i] = rb.GetBitmapNamed(kFramePartBitmapIds[i]); | |
| 239 initialized = true; | |
| 240 } | |
| 241 } | |
| 242 | |
| 243 static SkBitmap* standard_frame_bitmaps_[FRAME_PART_BITMAP_COUNT]; | |
| 244 | |
| 245 DISALLOW_EVIL_CONSTRUCTORS(OTRInactiveWindowResources); | |
| 246 }; | |
| 247 | |
| 248 // static | |
| 249 SkBitmap* ActiveWindowResources::standard_frame_bitmaps_[]; | |
| 250 SkBitmap* InactiveWindowResources::standard_frame_bitmaps_[]; | |
| 251 SkBitmap* OTRActiveWindowResources::standard_frame_bitmaps_[]; | |
| 252 SkBitmap* OTRInactiveWindowResources::standard_frame_bitmaps_[]; | |
| 253 | |
| 254 views::WindowResources* OpaqueNonClientView::active_resources_ = NULL; | |
| 255 views::WindowResources* OpaqueNonClientView::inactive_resources_ = NULL; | |
| 256 views::WindowResources* OpaqueNonClientView::active_otr_resources_ = NULL; | |
| 257 views::WindowResources* OpaqueNonClientView::inactive_otr_resources_ = NULL; | |
| 258 SkBitmap* OpaqueNonClientView::distributor_logo_ = NULL; | |
| 259 ChromeFont OpaqueNonClientView::title_font_; | |
| 260 | |
| 261 namespace { | |
| 262 // The frame border is only visible in restored mode and is hardcoded to 4 px on | |
| 263 // each side regardless of the system window border size. | |
| 264 const int kFrameBorderThickness = 4; | |
| 265 // Besides the frame border, there's another 11 px of empty space atop the | |
| 266 // window in restored mode, to use to drag the window around. | |
| 267 const int kNonClientRestoredExtraThickness = 11; | |
| 268 // While resize areas on Windows are normally the same size as the window | |
| 269 // borders, our top area is shrunk by 1 px to make it easier to move the window | |
| 270 // around with our thinner top grabbable strip. (Incidentally, our side and | |
| 271 // bottom resize areas don't match the frame border thickness either -- they | |
| 272 // span the whole nonclient area, so there's no "dead zone" for the mouse.) | |
| 273 const int kTopResizeAdjust = 1; | |
| 274 // In the window corners, the resize areas don't actually expand bigger, but the | |
| 275 // 16 px at the end of each edge triggers diagonal resizing. | |
| 276 const int kResizeAreaCornerSize = 16; | |
| 277 // The titlebar never shrinks to less than 18 px tall, plus the height of the | |
| 278 // frame border and any bottom edge. | |
| 279 const int kTitlebarMinimumHeight = 18; | |
| 280 // The icon is inset 2 px from the left frame border. | |
| 281 const int kIconLeftSpacing = 2; | |
| 282 // The icon takes up 16/25th of the available titlebar height. (This is | |
| 283 // expressed as two ints to avoid precision losses leading to off-by-one pixel | |
| 284 // errors.) | |
| 285 const int kIconHeightFractionNumerator = 16; | |
| 286 const int kIconHeightFractionDenominator = 25; | |
| 287 // The icon never shrinks below 16 px on a side. | |
| 288 const int kIconMinimumSize = 16; | |
| 289 // Because our frame border has a different "3D look" than Windows', with a less | |
| 290 // cluttered top edge, we need to shift the icon up by 1 px in restored mode so | |
| 291 // it looks more centered. | |
| 292 const int kIconRestoredAdjust = 1; | |
| 293 // There is a 4 px gap between the icon and the title text. | |
| 294 const int kIconTitleSpacing = 4; | |
| 295 // The title text starts 2 px below the bottom of the top frame border. | |
| 296 const int kTitleTopSpacing = 2; | |
| 297 // There is a 5 px gap between the title text and the distributor logo (if | |
| 298 // present) or caption buttons. | |
| 299 const int kTitleLogoSpacing = 5; | |
| 300 // In maximized mode, the OTR avatar starts 2 px below the top of the screen, so | |
| 301 // that it doesn't extend into the "3D edge" portion of the titlebar. | |
| 302 const int kOTRMaximizedTopSpacing = 2; | |
| 303 // The OTR avatar ends 2 px above the bottom of the tabstrip (which, given the | |
| 304 // way the tabstrip draws its bottom edge, will appear like a 1 px gap to the | |
| 305 // user). | |
| 306 const int kOTRBottomSpacing = 2; | |
| 307 // There are 2 px on each side of the OTR avatar (between the frame border and | |
| 308 // it on the left, and between it and the tabstrip on the right). | |
| 309 const int kOTRSideSpacing = 2; | |
| 310 // In restored mode, the New Tab button isn't at the same height as the caption | |
| 311 // buttons, but the space will look cluttered if it actually slides under them, | |
| 312 // so we stop it when the gap between the two is down to 5 px. | |
| 313 const int kNewTabCaptionRestoredSpacing = 5; | |
| 314 // In maximized mode, where the New Tab button and the caption buttons are at | |
| 315 // similar vertical coordinates, we need to reserve a larger, 16 px gap to avoid | |
| 316 // looking too cluttered. | |
| 317 const int kNewTabCaptionMaximizedSpacing = 16; | |
| 318 // When there's a distributor logo, we leave a 7 px gap between it and the | |
| 319 // caption buttons. | |
| 320 const int kLogoCaptionSpacing = 7; | |
| 321 // The caption buttons are always drawn 1 px down from the visible top of the | |
| 322 // window (the true top in restored mode, or the top of the screen in maximized | |
| 323 // mode). | |
| 324 const int kCaptionTopSpacing = 1; | |
| 325 } | |
| 326 | |
| 327 /////////////////////////////////////////////////////////////////////////////// | |
| 328 // OpaqueNonClientView, public: | |
| 329 | |
| 330 OpaqueNonClientView::OpaqueNonClientView(OpaqueFrame* frame, | |
| 331 BrowserView* browser_view) | |
| 332 : NonClientView(), | |
| 333 minimize_button_(new views::Button), | |
| 334 maximize_button_(new views::Button), | |
| 335 restore_button_(new views::Button), | |
| 336 close_button_(new views::Button), | |
| 337 window_icon_(NULL), | |
| 338 frame_(frame), | |
| 339 browser_view_(browser_view) { | |
| 340 InitClass(); | |
| 341 if (browser_view->IsOffTheRecord()) { | |
| 342 if (!active_otr_resources_) { | |
| 343 // Lazy load OTR resources only when we first show an OTR frame. | |
| 344 active_otr_resources_ = new OTRActiveWindowResources; | |
| 345 inactive_otr_resources_ = new OTRInactiveWindowResources; | |
| 346 } | |
| 347 current_active_resources_ = active_otr_resources_; | |
| 348 current_inactive_resources_= inactive_otr_resources_; | |
| 349 } else { | |
| 350 current_active_resources_ = active_resources_; | |
| 351 current_inactive_resources_ = inactive_resources_; | |
| 352 } | |
| 353 | |
| 354 views::WindowResources* resources = current_active_resources_; | |
| 355 minimize_button_->SetImage( | |
| 356 views::Button::BS_NORMAL, | |
| 357 resources->GetPartBitmap(FRAME_MINIMIZE_BUTTON_ICON)); | |
| 358 minimize_button_->SetImage( | |
| 359 views::Button::BS_HOT, | |
| 360 resources->GetPartBitmap(FRAME_MINIMIZE_BUTTON_ICON_H)); | |
| 361 minimize_button_->SetImage( | |
| 362 views::Button::BS_PUSHED, | |
| 363 resources->GetPartBitmap(FRAME_MINIMIZE_BUTTON_ICON_P)); | |
| 364 minimize_button_->SetListener(this, -1); | |
| 365 minimize_button_->SetAccessibleName( | |
| 366 l10n_util::GetString(IDS_ACCNAME_MINIMIZE)); | |
| 367 AddChildView(minimize_button_); | |
| 368 | |
| 369 maximize_button_->SetImage( | |
| 370 views::Button::BS_NORMAL, | |
| 371 resources->GetPartBitmap(FRAME_MAXIMIZE_BUTTON_ICON)); | |
| 372 maximize_button_->SetImage( | |
| 373 views::Button::BS_HOT, | |
| 374 resources->GetPartBitmap(FRAME_MAXIMIZE_BUTTON_ICON_H)); | |
| 375 maximize_button_->SetImage( | |
| 376 views::Button::BS_PUSHED, | |
| 377 resources->GetPartBitmap(FRAME_MAXIMIZE_BUTTON_ICON_P)); | |
| 378 maximize_button_->SetListener(this, -1); | |
| 379 maximize_button_->SetAccessibleName( | |
| 380 l10n_util::GetString(IDS_ACCNAME_MAXIMIZE)); | |
| 381 AddChildView(maximize_button_); | |
| 382 | |
| 383 restore_button_->SetImage( | |
| 384 views::Button::BS_NORMAL, | |
| 385 resources->GetPartBitmap(FRAME_RESTORE_BUTTON_ICON)); | |
| 386 restore_button_->SetImage( | |
| 387 views::Button::BS_HOT, | |
| 388 resources->GetPartBitmap(FRAME_RESTORE_BUTTON_ICON_H)); | |
| 389 restore_button_->SetImage( | |
| 390 views::Button::BS_PUSHED, | |
| 391 resources->GetPartBitmap(FRAME_RESTORE_BUTTON_ICON_P)); | |
| 392 restore_button_->SetListener(this, -1); | |
| 393 restore_button_->SetAccessibleName( | |
| 394 l10n_util::GetString(IDS_ACCNAME_RESTORE)); | |
| 395 AddChildView(restore_button_); | |
| 396 | |
| 397 close_button_->SetImage( | |
| 398 views::Button::BS_NORMAL, | |
| 399 resources->GetPartBitmap(FRAME_CLOSE_BUTTON_ICON)); | |
| 400 close_button_->SetImage( | |
| 401 views::Button::BS_HOT, | |
| 402 resources->GetPartBitmap(FRAME_CLOSE_BUTTON_ICON_H)); | |
| 403 close_button_->SetImage( | |
| 404 views::Button::BS_PUSHED, | |
| 405 resources->GetPartBitmap(FRAME_CLOSE_BUTTON_ICON_P)); | |
| 406 close_button_->SetListener(this, -1); | |
| 407 close_button_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_CLOSE)); | |
| 408 AddChildView(close_button_); | |
| 409 | |
| 410 // Initializing the TabIconView is expensive, so only do it if we need to. | |
| 411 if (browser_view_->ShouldShowWindowIcon()) { | |
| 412 window_icon_ = new TabIconView(this); | |
| 413 window_icon_->set_is_light(true); | |
| 414 AddChildView(window_icon_); | |
| 415 window_icon_->Update(); | |
| 416 } | |
| 417 // Only load the title font if we're going to need to use it to paint. | |
| 418 // Loading fonts is expensive. | |
| 419 if (browser_view_->ShouldShowWindowTitle()) | |
| 420 InitAppWindowResources(); | |
| 421 } | |
| 422 | |
| 423 OpaqueNonClientView::~OpaqueNonClientView() { | |
| 424 } | |
| 425 | |
| 426 gfx::Rect OpaqueNonClientView::GetWindowBoundsForClientBounds( | |
| 427 const gfx::Rect& client_bounds) { | |
| 428 int top_height = NonClientTopBorderHeight(); | |
| 429 int border_thickness = NonClientBorderThickness(); | |
| 430 return gfx::Rect(std::max(0, client_bounds.x() - border_thickness), | |
| 431 std::max(0, client_bounds.y() - top_height), | |
| 432 client_bounds.width() + (2 * border_thickness), | |
| 433 client_bounds.height() + top_height + border_thickness); | |
| 434 } | |
| 435 | |
| 436 gfx::Rect OpaqueNonClientView::GetBoundsForTabStrip(TabStrip* tabstrip) { | |
| 437 int tabstrip_x = browser_view_->ShouldShowOffTheRecordAvatar() ? | |
| 438 (otr_avatar_bounds_.right() + kOTRSideSpacing) : | |
| 439 NonClientBorderThickness(); | |
| 440 int tabstrip_width = minimize_button_->x() - tabstrip_x - | |
| 441 (frame_->IsMaximized() ? | |
| 442 kNewTabCaptionMaximizedSpacing : kNewTabCaptionRestoredSpacing); | |
| 443 return gfx::Rect(tabstrip_x, NonClientTopBorderHeight(), | |
| 444 std::max(0, tabstrip_width), tabstrip->GetPreferredHeight()); | |
| 445 } | |
| 446 | |
| 447 void OpaqueNonClientView::UpdateWindowIcon() { | |
| 448 if (window_icon_) | |
| 449 window_icon_->Update(); | |
| 450 } | |
| 451 | |
| 452 /////////////////////////////////////////////////////////////////////////////// | |
| 453 // OpaqueNonClientView, views::NonClientView implementation: | |
| 454 | |
| 455 gfx::Rect OpaqueNonClientView::CalculateClientAreaBounds(int width, | |
| 456 int height) const { | |
| 457 int top_height = NonClientTopBorderHeight(); | |
| 458 int border_thickness = NonClientBorderThickness(); | |
| 459 return gfx::Rect(border_thickness, top_height, | |
| 460 std::max(0, width - (2 * border_thickness)), | |
| 461 std::max(0, height - top_height - border_thickness)); | |
| 462 } | |
| 463 | |
| 464 gfx::Size OpaqueNonClientView::CalculateWindowSizeForClientSize( | |
| 465 int width, | |
| 466 int height) const { | |
| 467 int border_thickness = NonClientBorderThickness(); | |
| 468 return gfx::Size(width + (2 * border_thickness), | |
| 469 height + NonClientTopBorderHeight() + border_thickness); | |
| 470 } | |
| 471 | |
| 472 gfx::Point OpaqueNonClientView::GetSystemMenuPoint() const { | |
| 473 gfx::Point system_menu_point(FrameBorderThickness(), | |
| 474 NonClientTopBorderHeight() + browser_view_->GetTabStripHeight() - | |
| 475 (browser_view_->IsFullscreen() ? 0 : kClientEdgeThickness)); | |
| 476 ConvertPointToScreen(this, &system_menu_point); | |
| 477 return system_menu_point; | |
| 478 } | |
| 479 | |
| 480 int OpaqueNonClientView::NonClientHitTest(const gfx::Point& point) { | |
| 481 if (!bounds().Contains(point)) | |
| 482 return HTNOWHERE; | |
| 483 | |
| 484 int frame_component = frame_->client_view()->NonClientHitTest(point); | |
| 485 if (frame_component != HTNOWHERE) | |
| 486 return frame_component; | |
| 487 | |
| 488 // Then see if the point is within any of the window controls. | |
| 489 if (close_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains(point)) | |
| 490 return HTCLOSE; | |
| 491 if (restore_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains( | |
| 492 point)) | |
| 493 return HTMAXBUTTON; | |
| 494 if (maximize_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains( | |
| 495 point)) | |
| 496 return HTMAXBUTTON; | |
| 497 if (minimize_button_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains( | |
| 498 point)) | |
| 499 return HTMINBUTTON; | |
| 500 if (window_icon_ && | |
| 501 window_icon_->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains(point)) | |
| 502 return HTSYSMENU; | |
| 503 | |
| 504 int window_component = GetHTComponentForFrame(point, TopResizeHeight(), | |
| 505 NonClientBorderThickness(), kResizeAreaCornerSize, kResizeAreaCornerSize, | |
| 506 frame_->window_delegate()->CanResize()); | |
| 507 // Fall back to the caption if no other component matches. | |
| 508 return (window_component == HTNOWHERE) ? HTCAPTION : window_component; | |
| 509 } | |
| 510 | |
| 511 void OpaqueNonClientView::GetWindowMask(const gfx::Size& size, | |
| 512 gfx::Path* window_mask) { | |
| 513 DCHECK(window_mask); | |
| 514 | |
| 515 if (browser_view_->IsFullscreen()) | |
| 516 return; | |
| 517 | |
| 518 // Redefine the window visible region for the new size. | |
| 519 window_mask->moveTo(0, 3); | |
| 520 window_mask->lineTo(1, 2); | |
| 521 window_mask->lineTo(1, 1); | |
| 522 window_mask->lineTo(2, 1); | |
| 523 window_mask->lineTo(3, 0); | |
| 524 | |
| 525 window_mask->lineTo(SkIntToScalar(size.width() - 3), 0); | |
| 526 window_mask->lineTo(SkIntToScalar(size.width() - 2), 1); | |
| 527 window_mask->lineTo(SkIntToScalar(size.width() - 1), 1); | |
| 528 window_mask->lineTo(SkIntToScalar(size.width() - 1), 2); | |
| 529 window_mask->lineTo(SkIntToScalar(size.width()), 3); | |
| 530 | |
| 531 window_mask->lineTo(SkIntToScalar(size.width()), | |
| 532 SkIntToScalar(size.height())); | |
| 533 window_mask->lineTo(0, SkIntToScalar(size.height())); | |
| 534 window_mask->close(); | |
| 535 } | |
| 536 | |
| 537 void OpaqueNonClientView::EnableClose(bool enable) { | |
| 538 close_button_->SetEnabled(enable); | |
| 539 } | |
| 540 | |
| 541 void OpaqueNonClientView::ResetWindowControls() { | |
| 542 restore_button_->SetState(views::Button::BS_NORMAL); | |
| 543 minimize_button_->SetState(views::Button::BS_NORMAL); | |
| 544 maximize_button_->SetState(views::Button::BS_NORMAL); | |
| 545 // The close button isn't affected by this constraint. | |
| 546 } | |
| 547 | |
| 548 /////////////////////////////////////////////////////////////////////////////// | |
| 549 // OpaqueNonClientView, views::View overrides: | |
| 550 | |
| 551 void OpaqueNonClientView::Paint(ChromeCanvas* canvas) { | |
| 552 if (browser_view_->IsFullscreen()) | |
| 553 return; // Nothing is visible, so don't bother to paint. | |
| 554 | |
| 555 if (frame_->IsMaximized()) | |
| 556 PaintMaximizedFrameBorder(canvas); | |
| 557 else | |
| 558 PaintRestoredFrameBorder(canvas); | |
| 559 PaintDistributorLogo(canvas); | |
| 560 PaintTitleBar(canvas); | |
| 561 PaintToolbarBackground(canvas); | |
| 562 PaintOTRAvatar(canvas); | |
| 563 if (!frame_->IsMaximized()) | |
| 564 PaintRestoredClientEdge(canvas); | |
| 565 } | |
| 566 | |
| 567 void OpaqueNonClientView::Layout() { | |
| 568 LayoutWindowControls(); | |
| 569 LayoutDistributorLogo(); | |
| 570 LayoutTitleBar(); | |
| 571 LayoutOTRAvatar(); | |
| 572 LayoutClientView(); | |
| 573 } | |
| 574 | |
| 575 views::View* OpaqueNonClientView::GetViewForPoint(const gfx::Point& point, | |
| 576 bool can_create_floating) { | |
| 577 // We override this function because the ClientView can overlap the non - | |
| 578 // client view, making it impossible to click on the window controls. We need | |
| 579 // to ensure the window controls are checked _first_. | |
| 580 views::View* views[] = | |
| 581 { close_button_, restore_button_, maximize_button_, minimize_button_ }; | |
| 582 for (int i = 0; i < arraysize(views); ++i) { | |
| 583 if (!views[i]->IsVisible()) | |
| 584 continue; | |
| 585 // Apply mirroring transformation on view bounds for RTL chrome. | |
| 586 if (views[i]->GetBounds(APPLY_MIRRORING_TRANSFORMATION).Contains(point)) | |
| 587 return views[i]; | |
| 588 } | |
| 589 return View::GetViewForPoint(point, can_create_floating); | |
| 590 } | |
| 591 | |
| 592 void OpaqueNonClientView::ViewHierarchyChanged(bool is_add, | |
| 593 views::View* parent, | |
| 594 views::View* child) { | |
| 595 if (is_add && child == this) { | |
| 596 DCHECK(GetWidget()); | |
| 597 DCHECK(frame_->client_view()->GetParent() != this); | |
| 598 AddChildView(frame_->client_view()); | |
| 599 | |
| 600 // The Accessibility glue looks for the product name on these two views to | |
| 601 // determine if this is in fact a Chrome window. | |
| 602 GetRootView()->SetAccessibleName(l10n_util::GetString(IDS_PRODUCT_NAME)); | |
| 603 SetAccessibleName(l10n_util::GetString(IDS_PRODUCT_NAME)); | |
| 604 } | |
| 605 } | |
| 606 | |
| 607 bool OpaqueNonClientView::GetAccessibleRole(VARIANT* role) { | |
| 608 DCHECK(role); | |
| 609 // We aren't actually the client area of the window, but we act like it as | |
| 610 // far as MSAA and the UI tests are concerned. | |
| 611 role->vt = VT_I4; | |
| 612 role->lVal = ROLE_SYSTEM_CLIENT; | |
| 613 return true; | |
| 614 } | |
| 615 | |
| 616 bool OpaqueNonClientView::GetAccessibleName(std::wstring* name) { | |
| 617 if (!accessible_name_.empty()) { | |
| 618 *name = accessible_name_; | |
| 619 return true; | |
| 620 } | |
| 621 return false; | |
| 622 } | |
| 623 | |
| 624 void OpaqueNonClientView::SetAccessibleName(const std::wstring& name) { | |
| 625 accessible_name_ = name; | |
| 626 } | |
| 627 | |
| 628 /////////////////////////////////////////////////////////////////////////////// | |
| 629 // OpaqueNonClientView, views::BaseButton::ButtonListener implementation: | |
| 630 | |
| 631 void OpaqueNonClientView::ButtonPressed(views::BaseButton* sender) { | |
| 632 if (sender == minimize_button_) | |
| 633 frame_->ExecuteSystemMenuCommand(SC_MINIMIZE); | |
| 634 else if (sender == maximize_button_) | |
| 635 frame_->ExecuteSystemMenuCommand(SC_MAXIMIZE); | |
| 636 else if (sender == restore_button_) | |
| 637 frame_->ExecuteSystemMenuCommand(SC_RESTORE); | |
| 638 else if (sender == close_button_) | |
| 639 frame_->ExecuteSystemMenuCommand(SC_CLOSE); | |
| 640 } | |
| 641 | |
| 642 /////////////////////////////////////////////////////////////////////////////// | |
| 643 // OpaqueNonClientView, TabIconView::TabContentsProvider implementation: | |
| 644 | |
| 645 bool OpaqueNonClientView::ShouldTabIconViewAnimate() const { | |
| 646 // This function is queried during the creation of the window as the | |
| 647 // TabIconView we host is initialized, so we need to NULL check the selected | |
| 648 // TabContents because in this condition there is not yet a selected tab. | |
| 649 TabContents* current_tab = browser_view_->GetSelectedTabContents(); | |
| 650 return current_tab ? current_tab->is_loading() : false; | |
| 651 } | |
| 652 | |
| 653 SkBitmap OpaqueNonClientView::GetFavIconForTabIconView() { | |
| 654 return frame_->window_delegate()->GetWindowIcon(); | |
| 655 } | |
| 656 | |
| 657 /////////////////////////////////////////////////////////////////////////////// | |
| 658 // OpaqueNonClientView, private: | |
| 659 | |
| 660 int OpaqueNonClientView::FrameBorderThickness() const { | |
| 661 if (browser_view_->IsFullscreen()) | |
| 662 return 0; | |
| 663 return frame_->IsMaximized() ? | |
| 664 GetSystemMetrics(SM_CXSIZEFRAME) : kFrameBorderThickness; | |
| 665 } | |
| 666 | |
| 667 int OpaqueNonClientView::TopResizeHeight() const { | |
| 668 return FrameBorderThickness() - kTopResizeAdjust; | |
| 669 } | |
| 670 | |
| 671 int OpaqueNonClientView::NonClientBorderThickness() const { | |
| 672 // When we fill the screen, we don't show a client edge. | |
| 673 return FrameBorderThickness() + | |
| 674 (browser_view_->CanCurrentlyResize() ? kClientEdgeThickness : 0); | |
| 675 } | |
| 676 | |
| 677 int OpaqueNonClientView::NonClientTopBorderHeight() const { | |
| 678 if (frame_->window_delegate()->ShouldShowWindowTitle()) { | |
| 679 int title_top_spacing, title_thickness; | |
| 680 return TitleCoordinates(&title_top_spacing, &title_thickness); | |
| 681 } | |
| 682 | |
| 683 return FrameBorderThickness() + (browser_view_->CanCurrentlyResize() ? | |
| 684 kNonClientRestoredExtraThickness : 0); | |
| 685 } | |
| 686 | |
| 687 int OpaqueNonClientView::UnavailablePixelsAtBottomOfNonClientHeight() const { | |
| 688 // Tricky: When a toolbar is edging the titlebar, it not only draws its own | |
| 689 // shadow and client edge, but an extra, light "shadow" pixel as well, which | |
| 690 // is treated as available space. Thus the nonclient area actually _fails_ to | |
| 691 // include some available pixels, leading to a negative number here. | |
| 692 if (browser_view_->IsToolbarVisible()) | |
| 693 return -kFrameShadowThickness; | |
| 694 | |
| 695 return kFrameShadowThickness + | |
| 696 (frame_->IsMaximized() ? 0 : kClientEdgeThickness); | |
| 697 } | |
| 698 | |
| 699 int OpaqueNonClientView::TitleCoordinates(int* title_top_spacing, | |
| 700 int* title_thickness) const { | |
| 701 int frame_thickness = FrameBorderThickness(); | |
| 702 int min_titlebar_height = kTitlebarMinimumHeight + frame_thickness; | |
| 703 *title_top_spacing = frame_thickness + kTitleTopSpacing; | |
| 704 // The bottom spacing should be the same apparent height as the top spacing. | |
| 705 // Because the actual top spacing height varies based on the system border | |
| 706 // thickness, we calculate this based on the restored top spacing and then | |
| 707 // adjust for maximized mode. We also don't include the frame shadow here, | |
| 708 // since while it's part of the bottom spacing it will be added in at the end | |
| 709 // as necessary (when a toolbar is present, the "shadow" is actually drawn by | |
| 710 // the toolbar). | |
| 711 int title_bottom_spacing = | |
| 712 kFrameBorderThickness + kTitleTopSpacing - kFrameShadowThickness; | |
| 713 if (frame_->IsMaximized()) { | |
| 714 // When we maximize, the top border appears to be chopped off; shift the | |
| 715 // title down to stay centered within the remaining space. | |
| 716 int title_adjust = (kFrameBorderThickness / 2); | |
| 717 *title_top_spacing += title_adjust; | |
| 718 title_bottom_spacing -= title_adjust; | |
| 719 } | |
| 720 *title_thickness = std::max(title_font_.height(), | |
| 721 min_titlebar_height - *title_top_spacing - title_bottom_spacing); | |
| 722 return *title_top_spacing + *title_thickness + title_bottom_spacing + | |
| 723 UnavailablePixelsAtBottomOfNonClientHeight(); | |
| 724 } | |
| 725 | |
| 726 void OpaqueNonClientView::PaintRestoredFrameBorder(ChromeCanvas* canvas) { | |
| 727 SkBitmap* top_left_corner = resources()->GetPartBitmap(FRAME_TOP_LEFT_CORNER); | |
| 728 SkBitmap* top_right_corner = | |
| 729 resources()->GetPartBitmap(FRAME_TOP_RIGHT_CORNER); | |
| 730 SkBitmap* top_edge = resources()->GetPartBitmap(FRAME_TOP_EDGE); | |
| 731 SkBitmap* right_edge = resources()->GetPartBitmap(FRAME_RIGHT_EDGE); | |
| 732 SkBitmap* left_edge = resources()->GetPartBitmap(FRAME_LEFT_EDGE); | |
| 733 SkBitmap* bottom_left_corner = | |
| 734 resources()->GetPartBitmap(FRAME_BOTTOM_LEFT_CORNER); | |
| 735 SkBitmap* bottom_right_corner = | |
| 736 resources()->GetPartBitmap(FRAME_BOTTOM_RIGHT_CORNER); | |
| 737 SkBitmap* bottom_edge = resources()->GetPartBitmap(FRAME_BOTTOM_EDGE); | |
| 738 | |
| 739 // Top. | |
| 740 canvas->DrawBitmapInt(*top_left_corner, 0, 0); | |
| 741 canvas->TileImageInt(*top_edge, top_left_corner->width(), 0, | |
| 742 width() - top_right_corner->width(), top_edge->height()); | |
| 743 canvas->DrawBitmapInt(*top_right_corner, | |
| 744 width() - top_right_corner->width(), 0); | |
| 745 // Note: When we don't have a toolbar, we need to draw some kind of bottom | |
| 746 // edge here. Because the App Window graphics we use for this have an | |
| 747 // attached client edge and their sizing algorithm is a little involved, we do | |
| 748 // all this in PaintRestoredClientEdge(). | |
| 749 | |
| 750 // Right. | |
| 751 canvas->TileImageInt(*right_edge, width() - right_edge->width(), | |
| 752 top_right_corner->height(), right_edge->width(), | |
| 753 height() - top_right_corner->height() - | |
| 754 bottom_right_corner->height()); | |
| 755 | |
| 756 // Bottom. | |
| 757 canvas->DrawBitmapInt(*bottom_right_corner, | |
| 758 width() - bottom_right_corner->width(), | |
| 759 height() - bottom_right_corner->height()); | |
| 760 canvas->TileImageInt(*bottom_edge, bottom_left_corner->width(), | |
| 761 height() - bottom_edge->height(), | |
| 762 width() - bottom_left_corner->width() - | |
| 763 bottom_right_corner->width(), | |
| 764 bottom_edge->height()); | |
| 765 canvas->DrawBitmapInt(*bottom_left_corner, 0, | |
| 766 height() - bottom_left_corner->height()); | |
| 767 | |
| 768 // Left. | |
| 769 canvas->TileImageInt(*left_edge, 0, top_left_corner->height(), | |
| 770 left_edge->width(), | |
| 771 height() - top_left_corner->height() - bottom_left_corner->height()); | |
| 772 } | |
| 773 | |
| 774 void OpaqueNonClientView::PaintMaximizedFrameBorder(ChromeCanvas* canvas) { | |
| 775 SkBitmap* top_edge = resources()->GetPartBitmap(FRAME_TOP_EDGE); | |
| 776 canvas->TileImageInt(*top_edge, 0, FrameBorderThickness(), width(), | |
| 777 top_edge->height()); | |
| 778 | |
| 779 if (!browser_view_->IsToolbarVisible()) { | |
| 780 // There's no toolbar to edge the frame border, so we need to draw a bottom | |
| 781 // edge. The graphic we use for this has a built in client edge, so we clip | |
| 782 // it off the bottom. | |
| 783 SkBitmap* top_center = | |
| 784 resources()->GetPartBitmap(FRAME_NO_TOOLBAR_TOP_CENTER); | |
| 785 int edge_height = top_center->height() - kClientEdgeThickness; | |
| 786 canvas->TileImageInt(*top_center, 0, | |
| 787 frame_->client_view()->y() - edge_height, width(), edge_height); | |
| 788 } | |
| 789 } | |
| 790 | |
| 791 void OpaqueNonClientView::PaintDistributorLogo(ChromeCanvas* canvas) { | |
| 792 // The distributor logo is only painted when the frame is not maximized and | |
| 793 // when we actually have a logo. | |
| 794 if (!frame_->IsMaximized() && distributor_logo_) { | |
| 795 canvas->DrawBitmapInt(*distributor_logo_, | |
| 796 MirroredLeftPointForRect(logo_bounds_), logo_bounds_.y()); | |
| 797 } | |
| 798 } | |
| 799 | |
| 800 void OpaqueNonClientView::PaintTitleBar(ChromeCanvas* canvas) { | |
| 801 // The window icon is painted by the TabIconView. | |
| 802 views::WindowDelegate* d = frame_->window_delegate(); | |
| 803 if (d->ShouldShowWindowTitle()) { | |
| 804 canvas->DrawStringInt(d->GetWindowTitle(), title_font_, SK_ColorWHITE, | |
| 805 MirroredLeftPointForRect(title_bounds_), title_bounds_.y(), | |
| 806 title_bounds_.width(), title_bounds_.height()); | |
| 807 /* TODO(pkasting): If this window is active, we should also draw a drop | |
| 808 * shadow on the title. This is tricky, because we don't want to hardcode a | |
| 809 * shadow color (since we want to work with various themes), but we can't | |
| 810 * alpha-blend either (since the Windows text APIs don't really do this). | |
| 811 * So we'd need to sample the background color at the right location and | |
| 812 * synthesize a good shadow color. */ | |
| 813 } | |
| 814 } | |
| 815 | |
| 816 void OpaqueNonClientView::PaintToolbarBackground(ChromeCanvas* canvas) { | |
| 817 if (!browser_view_->IsToolbarVisible()) | |
| 818 return; | |
| 819 | |
| 820 gfx::Rect toolbar_bounds(browser_view_->GetToolbarBounds()); | |
| 821 gfx::Point toolbar_origin(toolbar_bounds.origin()); | |
| 822 View::ConvertPointToView(frame_->client_view(), this, &toolbar_origin); | |
| 823 toolbar_bounds.set_origin(toolbar_origin); | |
| 824 | |
| 825 SkBitmap* toolbar_left = | |
| 826 resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP_LEFT); | |
| 827 canvas->DrawBitmapInt(*toolbar_left, | |
| 828 toolbar_bounds.x() - toolbar_left->width(), | |
| 829 toolbar_bounds.y()); | |
| 830 | |
| 831 // Gross hack: We split the toolbar image into two pieces, since sometimes | |
| 832 // (popup mode) the toolbar isn't tall enough to show the whole image. The | |
| 833 // split happens between the top shadow section and the bottom gradient | |
| 834 // section so that we never break the gradient. | |
| 835 int split_point = kFrameShadowThickness * 2; | |
| 836 SkBitmap* toolbar_center = | |
| 837 resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP); | |
| 838 canvas->TileImageInt(*toolbar_center, 0, 0, toolbar_bounds.x(), | |
| 839 toolbar_bounds.y(), toolbar_bounds.width(), split_point); | |
| 840 canvas->TileImageInt(*toolbar_center, 0, | |
| 841 toolbar_center->height() - toolbar_bounds.height() + split_point, | |
| 842 toolbar_bounds.x(), toolbar_bounds.y() + split_point, | |
| 843 toolbar_bounds.width(), toolbar_bounds.height() - split_point); | |
| 844 | |
| 845 canvas->DrawBitmapInt( | |
| 846 *resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP_RIGHT), | |
| 847 toolbar_bounds.right(), toolbar_bounds.y()); | |
| 848 } | |
| 849 | |
| 850 void OpaqueNonClientView::PaintOTRAvatar(ChromeCanvas* canvas) { | |
| 851 if (!browser_view_->ShouldShowOffTheRecordAvatar()) | |
| 852 return; | |
| 853 | |
| 854 SkBitmap otr_avatar_icon = browser_view_->GetOTRAvatarIcon(); | |
| 855 canvas->DrawBitmapInt(otr_avatar_icon, 0, | |
| 856 (otr_avatar_icon.height() - otr_avatar_bounds_.height()) / 2, | |
| 857 otr_avatar_bounds_.width(), otr_avatar_bounds_.height(), | |
| 858 MirroredLeftPointForRect(otr_avatar_bounds_), otr_avatar_bounds_.y(), | |
| 859 otr_avatar_bounds_.width(), otr_avatar_bounds_.height(), false); | |
| 860 } | |
| 861 | |
| 862 void OpaqueNonClientView::PaintRestoredClientEdge(ChromeCanvas* canvas) { | |
| 863 int client_area_top = frame_->client_view()->y(); | |
| 864 | |
| 865 gfx::Rect client_area_bounds = CalculateClientAreaBounds(width(), height()); | |
| 866 if (browser_view_->IsToolbarVisible()) { | |
| 867 // The client edges start below the toolbar upper corner images regardless | |
| 868 // of how tall the toolbar itself is. | |
| 869 client_area_top += browser_view_->GetToolbarBounds().y() + | |
| 870 resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP_LEFT)->height(); | |
| 871 } else { | |
| 872 // The toolbar isn't going to draw a client edge for us, so draw one | |
| 873 // ourselves. | |
| 874 // This next calculation is necessary because the top center bitmap is | |
| 875 // shorter than the top left and right bitmaps. We need their top edges to | |
| 876 // line up, and we need the left and right edges to start below the corners' | |
| 877 // bottoms. | |
| 878 SkBitmap* top_center = | |
| 879 resources()->GetPartBitmap(FRAME_NO_TOOLBAR_TOP_CENTER); | |
| 880 SkBitmap* top_left = resources()->GetPartBitmap(FRAME_NO_TOOLBAR_TOP_LEFT); | |
| 881 int top_edge_y = client_area_top - top_center->height(); | |
| 882 client_area_top = top_edge_y + top_left->height(); | |
| 883 canvas->DrawBitmapInt(*top_left, client_area_bounds.x() - top_left->width(), | |
| 884 top_edge_y); | |
| 885 canvas->TileImageInt(*top_center, client_area_bounds.x(), top_edge_y, | |
| 886 client_area_bounds.width(), top_center->height()); | |
| 887 canvas->DrawBitmapInt( | |
| 888 *resources()->GetPartBitmap(FRAME_NO_TOOLBAR_TOP_RIGHT), | |
| 889 client_area_bounds.right(), top_edge_y); | |
| 890 } | |
| 891 | |
| 892 int client_area_bottom = | |
| 893 std::max(client_area_top, height() - NonClientBorderThickness()); | |
| 894 int client_area_height = client_area_bottom - client_area_top; | |
| 895 SkBitmap* right = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_RIGHT); | |
| 896 canvas->TileImageInt(*right, client_area_bounds.right(), client_area_top, | |
| 897 right->width(), client_area_height); | |
| 898 | |
| 899 canvas->DrawBitmapInt( | |
| 900 *resources()->GetPartBitmap(FRAME_CLIENT_EDGE_BOTTOM_RIGHT), | |
| 901 client_area_bounds.right(), client_area_bottom); | |
| 902 | |
| 903 SkBitmap* bottom = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_BOTTOM); | |
| 904 canvas->TileImageInt(*bottom, client_area_bounds.x(), | |
| 905 client_area_bottom, client_area_bounds.width(), | |
| 906 bottom->height()); | |
| 907 | |
| 908 SkBitmap* bottom_left = | |
| 909 resources()->GetPartBitmap(FRAME_CLIENT_EDGE_BOTTOM_LEFT); | |
| 910 canvas->DrawBitmapInt(*bottom_left, | |
| 911 client_area_bounds.x() - bottom_left->width(), client_area_bottom); | |
| 912 | |
| 913 SkBitmap* left = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_LEFT); | |
| 914 canvas->TileImageInt(*left, client_area_bounds.x() - left->width(), | |
| 915 client_area_top, left->width(), client_area_height); | |
| 916 } | |
| 917 | |
| 918 void OpaqueNonClientView::LayoutWindowControls() { | |
| 919 close_button_->SetImageAlignment(views::Button::ALIGN_LEFT, | |
| 920 views::Button::ALIGN_BOTTOM); | |
| 921 // Maximized buttons start at window top so that even if their images aren't | |
| 922 // drawn flush with the screen edge, they still obey Fitts' Law. | |
| 923 bool is_maximized = frame_->IsMaximized(); | |
| 924 int frame_thickness = FrameBorderThickness(); | |
| 925 int caption_y = is_maximized ? frame_thickness : kCaptionTopSpacing; | |
| 926 int top_extra_height = is_maximized ? kCaptionTopSpacing : 0; | |
| 927 // There should always be the same number of non-shadow pixels visible to the | |
| 928 // side of the caption buttons. In maximized mode we extend the rightmost | |
| 929 // button to the screen corner to obey Fitts' Law. | |
| 930 int right_extra_width = is_maximized ? | |
| 931 (kFrameBorderThickness - kFrameShadowThickness) : 0; | |
| 932 int right_spacing = is_maximized ? | |
| 933 (GetSystemMetrics(SM_CXSIZEFRAME) + right_extra_width) : frame_thickness; | |
| 934 gfx::Size close_button_size = close_button_->GetPreferredSize(); | |
| 935 close_button_->SetBounds(width() - close_button_size.width() - right_spacing, | |
| 936 caption_y, | |
| 937 close_button_size.width() + right_extra_width, | |
| 938 close_button_size.height() + top_extra_height); | |
| 939 | |
| 940 // When the window is restored, we show a maximized button; otherwise, we show | |
| 941 // a restore button. | |
| 942 bool is_restored = !is_maximized && !frame_->IsMinimized(); | |
| 943 views::Button* invisible_button = is_restored ? | |
| 944 restore_button_ : maximize_button_; | |
| 945 invisible_button->SetVisible(false); | |
| 946 | |
| 947 views::Button* visible_button = is_restored ? | |
| 948 maximize_button_ : restore_button_; | |
| 949 visible_button->SetVisible(true); | |
| 950 visible_button->SetImageAlignment(views::Button::ALIGN_LEFT, | |
| 951 views::Button::ALIGN_BOTTOM); | |
| 952 gfx::Size visible_button_size = visible_button->GetPreferredSize(); | |
| 953 visible_button->SetBounds(close_button_->x() - visible_button_size.width(), | |
| 954 caption_y, visible_button_size.width(), | |
| 955 visible_button_size.height() + top_extra_height); | |
| 956 | |
| 957 minimize_button_->SetVisible(true); | |
| 958 minimize_button_->SetImageAlignment(views::Button::ALIGN_LEFT, | |
| 959 views::Button::ALIGN_BOTTOM); | |
| 960 gfx::Size minimize_button_size = minimize_button_->GetPreferredSize(); | |
| 961 minimize_button_->SetBounds( | |
| 962 visible_button->x() - minimize_button_size.width(), caption_y, | |
| 963 minimize_button_size.width(), | |
| 964 minimize_button_size.height() + top_extra_height); | |
| 965 } | |
| 966 | |
| 967 void OpaqueNonClientView::LayoutDistributorLogo() { | |
| 968 // Always lay out the logo, even when it's not present, so we can lay out the | |
| 969 // window title based on its position. | |
| 970 if (distributor_logo_) { | |
| 971 logo_bounds_.SetRect(minimize_button_->x() - distributor_logo_->width() - | |
| 972 kLogoCaptionSpacing, TopResizeHeight(), distributor_logo_->width(), | |
| 973 distributor_logo_->height()); | |
| 974 } else { | |
| 975 logo_bounds_.SetRect(minimize_button_->x(), TopResizeHeight(), 0, 0); | |
| 976 } | |
| 977 } | |
| 978 | |
| 979 void OpaqueNonClientView::LayoutTitleBar() { | |
| 980 // Always lay out the icon, even when it's not present, so we can lay out the | |
| 981 // window title based on its position. | |
| 982 int frame_thickness = FrameBorderThickness(); | |
| 983 int icon_x = frame_thickness + kIconLeftSpacing; | |
| 984 | |
| 985 // The usable height of the titlebar area is the total height minus the top | |
| 986 // resize border and any edge area we draw at its bottom. | |
| 987 int title_top_spacing, title_thickness; | |
| 988 int top_height = TitleCoordinates(&title_top_spacing, &title_thickness); | |
| 989 int available_height = top_height - frame_thickness - | |
| 990 UnavailablePixelsAtBottomOfNonClientHeight(); | |
| 991 | |
| 992 // The icon takes up a constant fraction of the available height, down to a | |
| 993 // minimum size, and is always an even number of pixels on a side (presumably | |
| 994 // to make scaled icons look better). It's centered within the usable height. | |
| 995 int icon_size = std::max((available_height * kIconHeightFractionNumerator / | |
| 996 kIconHeightFractionDenominator) / 2 * 2, kIconMinimumSize); | |
| 997 int icon_y = ((available_height - icon_size) / 2) + frame_thickness; | |
| 998 | |
| 999 // Hack: Our frame border has a different "3D look" than Windows'. Theirs has | |
| 1000 // a more complex gradient on the top that they push their icon/title below; | |
| 1001 // then the maximized window cuts this off and the icon/title are centered in | |
| 1002 // the remaining space. Because the apparent shape of our border is simpler, | |
| 1003 // using the same positioning makes things look slightly uncentered with | |
| 1004 // restored windows, so we come up to compensate. | |
| 1005 if (!frame_->IsMaximized()) | |
| 1006 icon_y -= kIconRestoredAdjust; | |
| 1007 | |
| 1008 views::WindowDelegate* d = frame_->window_delegate(); | |
| 1009 if (!d->ShouldShowWindowIcon()) | |
| 1010 icon_size = 0; | |
| 1011 if (window_icon_) | |
| 1012 window_icon_->SetBounds(icon_x, icon_y, icon_size, icon_size); | |
| 1013 | |
| 1014 // Size the title, if visible. | |
| 1015 if (d->ShouldShowWindowTitle()) { | |
| 1016 int title_x = icon_x + icon_size + | |
| 1017 (d->ShouldShowWindowIcon() ? kIconTitleSpacing : 0); | |
| 1018 title_bounds_.SetRect(title_x, | |
| 1019 title_top_spacing + ((title_thickness - title_font_.height()) / 2), | |
| 1020 std::max(0, logo_bounds_.x() - kTitleLogoSpacing - title_x), | |
| 1021 title_font_.height()); | |
| 1022 } | |
| 1023 } | |
| 1024 | |
| 1025 void OpaqueNonClientView::LayoutOTRAvatar() { | |
| 1026 SkBitmap otr_avatar_icon = browser_view_->GetOTRAvatarIcon(); | |
| 1027 int top_height = NonClientTopBorderHeight(); | |
| 1028 int tabstrip_height, otr_height; | |
| 1029 if (browser_view_->IsTabStripVisible()) { | |
| 1030 tabstrip_height = browser_view_->GetTabStripHeight() - kOTRBottomSpacing; | |
| 1031 otr_height = frame_->IsMaximized() ? | |
| 1032 (tabstrip_height - kOTRMaximizedTopSpacing) : | |
| 1033 otr_avatar_icon.height(); | |
| 1034 } else { | |
| 1035 tabstrip_height = otr_height = 0; | |
| 1036 } | |
| 1037 otr_avatar_bounds_.SetRect(NonClientBorderThickness() + kOTRSideSpacing, | |
| 1038 top_height + tabstrip_height - otr_height, | |
| 1039 otr_avatar_icon.width(), otr_height); | |
| 1040 } | |
| 1041 | |
| 1042 void OpaqueNonClientView::LayoutClientView() { | |
| 1043 frame_->client_view()->SetBounds(CalculateClientAreaBounds(width(), | |
| 1044 height())); | |
| 1045 } | |
| 1046 | |
| 1047 // static | |
| 1048 void OpaqueNonClientView::InitClass() { | |
| 1049 static bool initialized = false; | |
| 1050 if (!initialized) { | |
| 1051 active_resources_ = new ActiveWindowResources; | |
| 1052 inactive_resources_ = new InactiveWindowResources; | |
| 1053 | |
| 1054 #if defined(GOOGLE_CHROME_BUILD) | |
| 1055 distributor_logo_ = ResourceBundle::GetSharedInstance(). | |
| 1056 GetBitmapNamed(IDR_DISTRIBUTOR_LOGO_LIGHT); | |
| 1057 #endif | |
| 1058 | |
| 1059 initialized = true; | |
| 1060 } | |
| 1061 } | |
| 1062 | |
| 1063 // static | |
| 1064 void OpaqueNonClientView::InitAppWindowResources() { | |
| 1065 static bool initialized = false; | |
| 1066 if (!initialized) { | |
| 1067 title_font_ = win_util::GetWindowTitleFont(); | |
| 1068 initialized = true; | |
| 1069 } | |
| 1070 } | |
| OLD | NEW |