OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/views/tabs/tab.h" | 5 #include "chrome/browser/ui/views/tabs/tab.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 #include <limits> | 8 #include <limits> |
9 #include <utility> | 9 #include <utility> |
10 | 10 |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
109 | 109 |
110 // The minimum opacity (out of 1) when a tab (either active or inactive) is | 110 // The minimum opacity (out of 1) when a tab (either active or inactive) is |
111 // throbbing in the immersive mode light strip. | 111 // throbbing in the immersive mode light strip. |
112 const double kImmersiveTabMinThrobOpacity = 0.66; | 112 const double kImmersiveTabMinThrobOpacity = 0.66; |
113 | 113 |
114 // Number of steps in the immersive mode loading animation. | 114 // Number of steps in the immersive mode loading animation. |
115 const int kImmersiveLoadingStepCount = 32; | 115 const int kImmersiveLoadingStepCount = 32; |
116 | 116 |
117 const char kTabCloseButtonName[] = "TabCloseButton"; | 117 const char kTabCloseButtonName[] = "TabCloseButton"; |
118 | 118 |
| 119 //////////////////////////////////////////////////////////////////////////////// |
| 120 // ImageCacheEntryMetadata |
| 121 // |
| 122 // All metadata necessary to uniquely identify a cached image. |
| 123 struct ImageCacheEntryMetadata { |
| 124 ImageCacheEntryMetadata(int resource_id, |
| 125 SkColor fill_color, |
| 126 SkColor stroke_color, |
| 127 ui::ScaleFactor scale_factor, |
| 128 const gfx::Size& size); |
| 129 |
| 130 ~ImageCacheEntryMetadata(); |
| 131 |
| 132 bool operator==(const ImageCacheEntryMetadata& rhs) const; |
| 133 |
| 134 int resource_id; // Only needed by pre-MD |
| 135 SkColor fill_color; // Both colors only needed by MD |
| 136 SkColor stroke_color; |
| 137 ui::ScaleFactor scale_factor; |
| 138 gfx::Size size; |
| 139 }; |
| 140 |
| 141 ImageCacheEntryMetadata::ImageCacheEntryMetadata(int resource_id, |
| 142 SkColor fill_color, |
| 143 SkColor stroke_color, |
| 144 ui::ScaleFactor scale_factor, |
| 145 const gfx::Size& size) |
| 146 : resource_id(resource_id), |
| 147 fill_color(fill_color), |
| 148 stroke_color(stroke_color), |
| 149 scale_factor(scale_factor), |
| 150 size(size) { |
| 151 DCHECK_NE(ui::SCALE_FACTOR_NONE, scale_factor); |
| 152 |
| 153 // Some fields are only relevant for pre-MD vs. MD. Erase the irrelevant ones |
| 154 // so they don't cause incorrect cache misses. |
| 155 // TODO(pkasting): Remove |resource_id| field when non-MD code is deleted. |
| 156 if (ui::MaterialDesignController::IsModeMaterial()) |
| 157 resource_id = 0; |
| 158 else |
| 159 fill_color = stroke_color = SK_ColorTRANSPARENT; |
| 160 } |
| 161 |
| 162 ImageCacheEntryMetadata::~ImageCacheEntryMetadata() {} |
| 163 |
| 164 bool ImageCacheEntryMetadata::operator==( |
| 165 const ImageCacheEntryMetadata& rhs) const { |
| 166 return resource_id == rhs.resource_id && fill_color == rhs.fill_color && |
| 167 stroke_color == rhs.stroke_color && scale_factor == rhs.scale_factor && |
| 168 size == rhs.size; |
| 169 } |
| 170 |
| 171 //////////////////////////////////////////////////////////////////////////////// |
| 172 // ImageCacheEntry and cache management |
| 173 // |
| 174 // A cached image and the metadata used to generate it. |
| 175 struct ImageCacheEntry { |
| 176 ImageCacheEntry(const ImageCacheEntryMetadata& metadata, |
| 177 const gfx::ImageSkia& image); |
| 178 ~ImageCacheEntry(); |
| 179 |
| 180 ImageCacheEntryMetadata metadata; |
| 181 gfx::ImageSkia image; |
| 182 }; |
| 183 |
| 184 ImageCacheEntry::ImageCacheEntry(const ImageCacheEntryMetadata& metadata, |
| 185 const gfx::ImageSkia& image) |
| 186 : metadata(metadata), image(image) {} |
| 187 |
| 188 ImageCacheEntry::~ImageCacheEntry() {} |
| 189 |
| 190 typedef std::list<ImageCacheEntry> ImageCache; |
| 191 |
| 192 // As the majority of the tabs are inactive, and painting tabs is slowish, |
| 193 // we cache a handful of the inactive tab backgrounds here. |
| 194 static ImageCache* g_image_cache = nullptr; |
| 195 |
| 196 struct TabImages { |
| 197 gfx::ImageSkia* image_l; |
| 198 gfx::ImageSkia* image_c; |
| 199 gfx::ImageSkia* image_r; |
| 200 int l_width; |
| 201 int r_width; |
| 202 }; |
| 203 static TabImages g_active_images = {0}; |
| 204 static TabImages g_inactive_images = {0}; |
| 205 static TabImages g_mask_images = {0}; |
| 206 |
| 207 // Loads the images to be used for the tab background. |
| 208 void LoadTabImages() { |
| 209 g_image_cache->clear(); |
| 210 |
| 211 // We're not letting people override tab images just yet. |
| 212 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
| 213 |
| 214 g_active_images.image_l = rb.GetImageSkiaNamed(IDR_TAB_ACTIVE_LEFT); |
| 215 g_active_images.image_c = rb.GetImageSkiaNamed(IDR_TAB_ACTIVE_CENTER); |
| 216 g_active_images.image_r = rb.GetImageSkiaNamed(IDR_TAB_ACTIVE_RIGHT); |
| 217 g_active_images.l_width = g_active_images.image_l->width(); |
| 218 g_active_images.r_width = g_active_images.image_r->width(); |
| 219 |
| 220 g_inactive_images.image_l = rb.GetImageSkiaNamed(IDR_TAB_INACTIVE_LEFT); |
| 221 g_inactive_images.image_c = rb.GetImageSkiaNamed(IDR_TAB_INACTIVE_CENTER); |
| 222 g_inactive_images.image_r = rb.GetImageSkiaNamed(IDR_TAB_INACTIVE_RIGHT); |
| 223 g_inactive_images.l_width = g_inactive_images.image_l->width(); |
| 224 g_inactive_images.r_width = g_inactive_images.image_r->width(); |
| 225 |
| 226 g_mask_images.image_l = rb.GetImageSkiaNamed(IDR_TAB_ALPHA_LEFT); |
| 227 g_mask_images.image_r = rb.GetImageSkiaNamed(IDR_TAB_ALPHA_RIGHT); |
| 228 g_mask_images.l_width = g_mask_images.image_l->width(); |
| 229 g_mask_images.r_width = g_mask_images.image_r->width(); |
| 230 } |
| 231 |
| 232 // Performs a one-time initialization of static resources such as tab images. |
| 233 void InitTabResources() { |
| 234 static bool initialized = false; |
| 235 if (initialized) |
| 236 return; |
| 237 |
| 238 initialized = true; |
| 239 g_image_cache = new ImageCache(); |
| 240 |
| 241 // Load the tab images once now, and maybe again later if the theme changes. |
| 242 LoadTabImages(); |
| 243 } |
| 244 |
| 245 //////////////////////////////////////////////////////////////////////////////// |
| 246 // Drawing and utility functions |
| 247 |
119 // Returns the width of the tab endcap at scale 1. More precisely, this is the | 248 // Returns the width of the tab endcap at scale 1. More precisely, this is the |
120 // width of the curve making up either the outer or inner edge of the stroke; | 249 // width of the curve making up either the outer or inner edge of the stroke; |
121 // since these two curves are horizontally offset by 1 px (regardless of scale), | 250 // since these two curves are horizontally offset by 1 px (regardless of scale), |
122 // the total width of the endcap from tab outer edge to the inside end of the | 251 // the total width of the endcap from tab outer edge to the inside end of the |
123 // stroke inner edge is (GetUnscaledEndcapWidth() * scale) + 1. | 252 // stroke inner edge is (GetUnscaledEndcapWidth() * scale) + 1. |
124 float GetUnscaledEndcapWidth() { | 253 float GetUnscaledEndcapWidth() { |
125 return GetLayoutInsets(TAB).left() - 0.5f; | 254 return GetLayoutInsets(TAB).left() - 0.5f; |
126 } | 255 } |
127 | 256 |
128 void DrawHighlight(gfx::Canvas* canvas, | 257 void DrawHighlight(gfx::Canvas* canvas, |
(...skipping 11 matching lines...) Expand all Loading... |
140 } | 269 } |
141 | 270 |
142 // Returns whether the favicon for the given URL should be colored according to | 271 // Returns whether the favicon for the given URL should be colored according to |
143 // the browser theme. | 272 // the browser theme. |
144 bool ShouldThemifyFaviconForUrl(const GURL& url) { | 273 bool ShouldThemifyFaviconForUrl(const GURL& url) { |
145 return url.SchemeIs(content::kChromeUIScheme) && | 274 return url.SchemeIs(content::kChromeUIScheme) && |
146 url.host() != chrome::kChromeUIHelpHost && | 275 url.host() != chrome::kChromeUIHelpHost && |
147 url.host() != chrome::kChromeUIUberHost; | 276 url.host() != chrome::kChromeUIUberHost; |
148 } | 277 } |
149 | 278 |
| 279 // Computes a path corresponding to the tab's content region inside the outer |
| 280 // stroke. |
| 281 void GetFillPath(float scale, const gfx::Size& size, SkPath* fill) { |
| 282 const float right = size.width() * scale; |
| 283 // The bottom of the tab needs to be pixel-aligned or else when we call |
| 284 // ClipPath with anti-aliasing enabled it can cause artifacts. |
| 285 const float bottom = std::ceil(size.height() * scale); |
| 286 const float unscaled_endcap_width = GetUnscaledEndcapWidth(); |
| 287 |
| 288 fill->moveTo(right - 1, bottom); |
| 289 fill->rCubicTo(-0.75 * scale, 0, -1.625 * scale, -0.5 * scale, -2 * scale, |
| 290 -1.5 * scale); |
| 291 fill->lineTo(right - 1 - (unscaled_endcap_width - 2) * scale, 2.5 * scale); |
| 292 // Prevent overdraw in the center near minimum width (only happens if |
| 293 // scale < 2). We could instead avoid this by increasing the tab inset |
| 294 // values, but that would shift all the content inward as well, unless we |
| 295 // then overlapped the content on the endcaps, by which point we'd have a |
| 296 // huge mess. |
| 297 const float scaled_endcap_width = 1 + unscaled_endcap_width * scale; |
| 298 const float overlap = scaled_endcap_width * 2 - right; |
| 299 const float offset = (overlap > 0) ? (overlap / 2) : 0; |
| 300 fill->rCubicTo(-0.375 * scale, -1 * scale, -1.25 * scale + offset, |
| 301 -1.5 * scale, -2 * scale + offset, -1.5 * scale); |
| 302 if (overlap < 0) |
| 303 fill->lineTo(scaled_endcap_width, scale); |
| 304 fill->rCubicTo(-0.75 * scale, 0, -1.625 * scale - offset, 0.5 * scale, |
| 305 -2 * scale - offset, 1.5 * scale); |
| 306 fill->lineTo(1 + 2 * scale, bottom - 1.5 * scale); |
| 307 fill->rCubicTo(-0.375 * scale, scale, -1.25 * scale, 1.5 * scale, -2 * scale, |
| 308 1.5 * scale); |
| 309 fill->close(); |
| 310 } |
| 311 |
| 312 // Computes a path corresponding to the tab's outer border for a given |scale| |
| 313 // and stores it in |path|. If |extend_to_top| is true, the path is extended |
| 314 // vertically to the top of the tab bounds. The caller uses this for Fitts' |
| 315 // Law purposes in maximized/fullscreen mode. |
| 316 void GetBorderPath(float scale, |
| 317 const gfx::Size& size, |
| 318 bool extend_to_top, |
| 319 SkPath* path) { |
| 320 const float top = scale - 1; |
| 321 const float right = size.width() * scale; |
| 322 const float bottom = size.height() * scale; |
| 323 const float unscaled_endcap_width = GetUnscaledEndcapWidth(); |
| 324 |
| 325 path->moveTo(0, bottom); |
| 326 path->rLineTo(0, -1); |
| 327 path->rCubicTo(0.75 * scale, 0, 1.625 * scale, -0.5 * scale, 2 * scale, |
| 328 -1.5 * scale); |
| 329 path->lineTo((unscaled_endcap_width - 2) * scale, top + 1.5 * scale); |
| 330 if (extend_to_top) { |
| 331 // Create the vertical extension by extending the side diagonals until they |
| 332 // reach the top of the bounds. |
| 333 const float dy = 2.5 * scale - 1; |
| 334 const float dx = Tab::GetInverseDiagonalSlope() * dy; |
| 335 path->rLineTo(dx, -dy); |
| 336 path->lineTo(right - (unscaled_endcap_width - 2) * scale - dx, 0); |
| 337 path->rLineTo(dx, dy); |
| 338 } else { |
| 339 path->rCubicTo(0.375 * scale, -scale, 1.25 * scale, -1.5 * scale, 2 * scale, |
| 340 -1.5 * scale); |
| 341 path->lineTo(right - unscaled_endcap_width * scale, top); |
| 342 path->rCubicTo(0.75 * scale, 0, 1.625 * scale, 0.5 * scale, 2 * scale, |
| 343 1.5 * scale); |
| 344 } |
| 345 path->lineTo(right - 2 * scale, bottom - 1 - 1.5 * scale); |
| 346 path->rCubicTo(0.375 * scale, scale, 1.25 * scale, 1.5 * scale, 2 * scale, |
| 347 1.5 * scale); |
| 348 path->rLineTo(0, 1); |
| 349 path->close(); |
| 350 } |
| 351 |
| 352 void PaintTabFill(gfx::Canvas* canvas, |
| 353 gfx::ImageSkia* fill_image, |
| 354 int x_offset, |
| 355 int y_offset, |
| 356 const gfx::Size& size, |
| 357 bool is_active) { |
| 358 const gfx::Insets tab_insets(GetLayoutInsets(TAB)); |
| 359 // If this isn't the foreground tab, don't draw over the toolbar, but do |
| 360 // include the 1 px divider stroke at the bottom. |
| 361 const int toolbar_overlap = is_active ? 0 : (tab_insets.bottom() - 1); |
| 362 |
| 363 // Draw left edge. |
| 364 gfx::ImageSkia tab_l = gfx::ImageSkiaOperations::CreateTiledImage( |
| 365 *fill_image, x_offset, y_offset, g_mask_images.l_width, size.height()); |
| 366 gfx::ImageSkia theme_l = gfx::ImageSkiaOperations::CreateMaskedImage( |
| 367 tab_l, *g_mask_images.image_l); |
| 368 canvas->DrawImageInt( |
| 369 theme_l, 0, 0, theme_l.width(), theme_l.height() - toolbar_overlap, 0, 0, |
| 370 theme_l.width(), theme_l.height() - toolbar_overlap, false); |
| 371 |
| 372 // Draw right edge. |
| 373 gfx::ImageSkia tab_r = gfx::ImageSkiaOperations::CreateTiledImage( |
| 374 *fill_image, x_offset + size.width() - g_mask_images.r_width, y_offset, |
| 375 g_mask_images.r_width, size.height()); |
| 376 gfx::ImageSkia theme_r = gfx::ImageSkiaOperations::CreateMaskedImage( |
| 377 tab_r, *g_mask_images.image_r); |
| 378 canvas->DrawImageInt(theme_r, 0, 0, theme_r.width(), |
| 379 theme_r.height() - toolbar_overlap, |
| 380 size.width() - theme_r.width(), 0, theme_r.width(), |
| 381 theme_r.height() - toolbar_overlap, false); |
| 382 |
| 383 // Draw center. Instead of masking out the top portion we simply skip over it |
| 384 // by incrementing by the top padding, since it's a simple rectangle. |
| 385 canvas->TileImageInt( |
| 386 *fill_image, x_offset + g_mask_images.l_width, |
| 387 y_offset + tab_insets.top(), g_mask_images.l_width, tab_insets.top(), |
| 388 size.width() - g_mask_images.l_width - g_mask_images.r_width, |
| 389 size.height() - tab_insets.top() - toolbar_overlap); |
| 390 } |
| 391 |
150 } // namespace | 392 } // namespace |
151 | 393 |
152 //////////////////////////////////////////////////////////////////////////////// | 394 //////////////////////////////////////////////////////////////////////////////// |
153 // FaviconCrashAnimation | 395 // FaviconCrashAnimation |
154 // | 396 // |
155 // A custom animation subclass to manage the favicon crash animation. | 397 // A custom animation subclass to manage the favicon crash animation. |
156 class Tab::FaviconCrashAnimation : public gfx::LinearAnimation, | 398 class Tab::FaviconCrashAnimation : public gfx::LinearAnimation, |
157 public gfx::AnimationDelegate { | 399 public gfx::AnimationDelegate { |
158 public: | 400 public: |
159 explicit FaviconCrashAnimation(Tab* target) | 401 explicit FaviconCrashAnimation(Tab* target) |
(...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
405 waiting_state_.color = | 647 waiting_state_.color = |
406 tp->GetColor(ThemeProperties::COLOR_TAB_THROBBER_WAITING); | 648 tp->GetColor(ThemeProperties::COLOR_TAB_THROBBER_WAITING); |
407 gfx::PaintThrobberSpinningAfterWaiting( | 649 gfx::PaintThrobberSpinningAfterWaiting( |
408 canvas, bounds, | 650 canvas, bounds, |
409 tp->GetColor(ThemeProperties::COLOR_TAB_THROBBER_SPINNING), | 651 tp->GetColor(ThemeProperties::COLOR_TAB_THROBBER_SPINNING), |
410 base::TimeTicks::Now() - loading_start_time_, &waiting_state_); | 652 base::TimeTicks::Now() - loading_start_time_, &waiting_state_); |
411 } | 653 } |
412 } | 654 } |
413 | 655 |
414 //////////////////////////////////////////////////////////////////////////////// | 656 //////////////////////////////////////////////////////////////////////////////// |
415 // Tab::ImageCacheEntryMetadata | |
416 | |
417 struct Tab::ImageCacheEntryMetadata { | |
418 ImageCacheEntryMetadata(int resource_id, | |
419 SkColor fill_color, | |
420 SkColor stroke_color, | |
421 ui::ScaleFactor scale_factor, | |
422 const gfx::Size& size); | |
423 | |
424 ~ImageCacheEntryMetadata(); | |
425 | |
426 // Making this a non-member would require a friend declaration in Tab. Bleh. | |
427 bool operator==(const ImageCacheEntryMetadata& rhs) const; | |
428 | |
429 int resource_id; // Only needed by pre-MD | |
430 SkColor fill_color; // Both colors only needed by MD | |
431 SkColor stroke_color; | |
432 ui::ScaleFactor scale_factor; | |
433 gfx::Size size; | |
434 }; | |
435 | |
436 Tab::ImageCacheEntryMetadata::ImageCacheEntryMetadata( | |
437 int resource_id, | |
438 SkColor fill_color, | |
439 SkColor stroke_color, | |
440 ui::ScaleFactor scale_factor, | |
441 const gfx::Size& size) | |
442 : resource_id(resource_id), | |
443 fill_color(fill_color), | |
444 stroke_color(stroke_color), | |
445 scale_factor(scale_factor), | |
446 size(size) { | |
447 DCHECK_NE(ui::SCALE_FACTOR_NONE, scale_factor); | |
448 | |
449 // Some fields are only relevant for pre-MD vs. MD. Erase the irrelevant ones | |
450 // so they don't cause incorrect cache misses. | |
451 // TODO(pkasting): Remove |resource_id| field when non-MD code is deleted. | |
452 if (ui::MaterialDesignController::IsModeMaterial()) | |
453 resource_id = 0; | |
454 else | |
455 fill_color = stroke_color = SK_ColorTRANSPARENT; | |
456 } | |
457 | |
458 Tab::ImageCacheEntryMetadata::~ImageCacheEntryMetadata() {} | |
459 | |
460 bool Tab::ImageCacheEntryMetadata::operator==( | |
461 const ImageCacheEntryMetadata& rhs) const { | |
462 return resource_id == rhs.resource_id && fill_color == rhs.fill_color && | |
463 stroke_color == rhs.stroke_color && scale_factor == rhs.scale_factor && | |
464 size == rhs.size; | |
465 } | |
466 | |
467 //////////////////////////////////////////////////////////////////////////////// | |
468 // Tab::ImageCacheEntry | |
469 | |
470 struct Tab::ImageCacheEntry { | |
471 ImageCacheEntry(const ImageCacheEntryMetadata& metadata, | |
472 const gfx::ImageSkia& image); | |
473 ~ImageCacheEntry(); | |
474 | |
475 ImageCacheEntryMetadata metadata; | |
476 gfx::ImageSkia image; | |
477 }; | |
478 | |
479 Tab::ImageCacheEntry::ImageCacheEntry(const ImageCacheEntryMetadata& metadata, | |
480 const gfx::ImageSkia& image) | |
481 : metadata(metadata), image(image) {} | |
482 | |
483 Tab::ImageCacheEntry::~ImageCacheEntry() {} | |
484 | |
485 //////////////////////////////////////////////////////////////////////////////// | |
486 // Tab, statics: | |
487 | |
488 // static | |
489 const char Tab::kViewClassName[] = "Tab"; | |
490 Tab::TabImages Tab::active_images_ = {0}; | |
491 Tab::TabImages Tab::inactive_images_ = {0}; | |
492 Tab::TabImages Tab::mask_images_ = {0}; | |
493 Tab::ImageCache* Tab::image_cache_ = NULL; | |
494 | |
495 //////////////////////////////////////////////////////////////////////////////// | |
496 // Tab, public: | 657 // Tab, public: |
497 | 658 |
| 659 // static |
| 660 const char Tab::kViewClassName[] = "Tab"; |
| 661 |
498 Tab::Tab(TabController* controller, gfx::AnimationContainer* container) | 662 Tab::Tab(TabController* controller, gfx::AnimationContainer* container) |
499 : controller_(controller), | 663 : controller_(controller), |
500 closing_(false), | 664 closing_(false), |
501 dragging_(false), | 665 dragging_(false), |
502 detached_(false), | 666 detached_(false), |
503 favicon_hiding_offset_(0), | 667 favicon_hiding_offset_(0), |
504 immersive_loading_step_(0), | 668 immersive_loading_step_(0), |
505 should_display_crashed_favicon_(false), | 669 should_display_crashed_favicon_(false), |
506 pulse_animation_(new gfx::ThrobAnimation(this)), | 670 pulse_animation_(new gfx::ThrobAnimation(this)), |
507 crash_icon_animation_(new FaviconCrashAnimation(this)), | 671 crash_icon_animation_(new FaviconCrashAnimation(this)), |
(...skipping 295 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
803 // When the window is maximized we don't want to shave off the edges or top | 967 // When the window is maximized we don't want to shave off the edges or top |
804 // shadow of the tab, such that the user can click anywhere along the top | 968 // shadow of the tab, such that the user can click anywhere along the top |
805 // edge of the screen to select a tab. Ditto for immersive fullscreen. | 969 // edge of the screen to select a tab. Ditto for immersive fullscreen. |
806 const views::Widget* widget = GetWidget(); | 970 const views::Widget* widget = GetWidget(); |
807 const bool extend_to_top = | 971 const bool extend_to_top = |
808 widget && (widget->IsMaximized() || widget->IsFullscreen()); | 972 widget && (widget->IsMaximized() || widget->IsFullscreen()); |
809 | 973 |
810 if (ui::MaterialDesignController::IsModeMaterial()) { | 974 if (ui::MaterialDesignController::IsModeMaterial()) { |
811 SkPath border; | 975 SkPath border; |
812 const float scale = GetWidget()->GetCompositor()->device_scale_factor(); | 976 const float scale = GetWidget()->GetCompositor()->device_scale_factor(); |
813 GetBorderPath(scale, extend_to_top, &border); | 977 GetBorderPath(scale, size(), extend_to_top, &border); |
814 mask->addPath(border, SkMatrix::MakeScale(1 / scale)); | 978 mask->addPath(border, SkMatrix::MakeScale(1 / scale)); |
815 } else { | 979 } else { |
816 // Hit mask constants. | 980 // Hit mask constants. |
817 const SkScalar kTabCapWidth = 15; | 981 const SkScalar kTabCapWidth = 15; |
818 const SkScalar kTabTopCurveWidth = 4; | 982 const SkScalar kTabTopCurveWidth = 4; |
819 const SkScalar kTabBottomCurveWidth = 3; | 983 const SkScalar kTabBottomCurveWidth = 3; |
820 #if defined(OS_MACOSX) | 984 #if defined(OS_MACOSX) |
821 // Mac's Cocoa UI doesn't have shadows. | 985 // Mac's Cocoa UI doesn't have shadows. |
822 const SkScalar kTabInset = 0; | 986 const SkScalar kTabInset = 0; |
823 #elif defined(TOOLKIT_VIEWS) | 987 #elif defined(TOOLKIT_VIEWS) |
(...skipping 461 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1285 PaintTabBackgroundUsingFillId(canvas, false, fill_id, has_custom_image, | 1449 PaintTabBackgroundUsingFillId(canvas, false, fill_id, has_custom_image, |
1286 y_offset); | 1450 y_offset); |
1287 return; | 1451 return; |
1288 } | 1452 } |
1289 | 1453 |
1290 const ImageCacheEntryMetadata metadata( | 1454 const ImageCacheEntryMetadata metadata( |
1291 fill_id, tp->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB), | 1455 fill_id, tp->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB), |
1292 controller_->GetToolbarTopSeparatorColor(), | 1456 controller_->GetToolbarTopSeparatorColor(), |
1293 ui::GetSupportedScaleFactor(canvas->image_scale()), size()); | 1457 ui::GetSupportedScaleFactor(canvas->image_scale()), size()); |
1294 auto it = std::find_if( | 1458 auto it = std::find_if( |
1295 image_cache_->begin(), image_cache_->end(), | 1459 g_image_cache->begin(), g_image_cache->end(), |
1296 [&metadata](const ImageCacheEntry& e) { return e.metadata == metadata; }); | 1460 [&metadata](const ImageCacheEntry& e) { return e.metadata == metadata; }); |
1297 if (it == image_cache_->end()) { | 1461 if (it == g_image_cache->end()) { |
1298 gfx::Canvas tmp_canvas(size(), canvas->image_scale(), false); | 1462 gfx::Canvas tmp_canvas(size(), canvas->image_scale(), false); |
1299 PaintTabBackgroundUsingFillId(&tmp_canvas, false, fill_id, false, y_offset); | 1463 PaintTabBackgroundUsingFillId(&tmp_canvas, false, fill_id, false, y_offset); |
1300 image_cache_->emplace_front(metadata, | 1464 g_image_cache->emplace_front(metadata, |
1301 gfx::ImageSkia(tmp_canvas.ExtractImageRep())); | 1465 gfx::ImageSkia(tmp_canvas.ExtractImageRep())); |
1302 if (image_cache_->size() > kMaxImageCacheSize) | 1466 if (g_image_cache->size() > kMaxImageCacheSize) |
1303 image_cache_->pop_back(); | 1467 g_image_cache->pop_back(); |
1304 it = image_cache_->begin(); | 1468 it = g_image_cache->begin(); |
1305 } | 1469 } |
1306 canvas->DrawImageInt(it->image, 0, 0); | 1470 canvas->DrawImageInt(it->image, 0, 0); |
1307 } | 1471 } |
1308 | 1472 |
1309 void Tab::PaintTabBackgroundUsingFillId(gfx::Canvas* canvas, | 1473 void Tab::PaintTabBackgroundUsingFillId(gfx::Canvas* canvas, |
1310 bool is_active, | 1474 bool is_active, |
1311 int fill_id, | 1475 int fill_id, |
1312 bool has_custom_image, | 1476 bool has_custom_image, |
1313 int y_offset) { | 1477 int y_offset) { |
1314 const ui::ThemeProvider* tp = GetThemeProvider(); | 1478 const ui::ThemeProvider* tp = GetThemeProvider(); |
(...skipping 11 matching lines...) Expand all Loading... |
1326 SkPoint hover_location(PointToSkPoint(hover_controller_.location())); | 1490 SkPoint hover_location(PointToSkPoint(hover_controller_.location())); |
1327 const SkColor hover_color = | 1491 const SkColor hover_color = |
1328 SkColorSetA(toolbar_color, hover_controller_.GetAlpha()); | 1492 SkColorSetA(toolbar_color, hover_controller_.GetAlpha()); |
1329 | 1493 |
1330 if (ui::MaterialDesignController::IsModeMaterial()) { | 1494 if (ui::MaterialDesignController::IsModeMaterial()) { |
1331 gfx::ScopedCanvas scoped_canvas(canvas); | 1495 gfx::ScopedCanvas scoped_canvas(canvas); |
1332 const float scale = canvas->UndoDeviceScaleFactor(); | 1496 const float scale = canvas->UndoDeviceScaleFactor(); |
1333 | 1497 |
1334 // Draw the fill. | 1498 // Draw the fill. |
1335 SkPath fill; | 1499 SkPath fill; |
1336 GetFillPath(scale, &fill); | 1500 GetFillPath(scale, size(), &fill); |
1337 SkPaint paint; | 1501 SkPaint paint; |
1338 paint.setAntiAlias(true); | 1502 paint.setAntiAlias(true); |
1339 { | 1503 { |
1340 gfx::ScopedCanvas clip_scoper(canvas); | 1504 gfx::ScopedCanvas clip_scoper(canvas); |
1341 canvas->ClipPath(fill, true); | 1505 canvas->ClipPath(fill, true); |
1342 if (has_custom_image) { | 1506 if (has_custom_image) { |
1343 gfx::ScopedCanvas scale_scoper(canvas); | 1507 gfx::ScopedCanvas scale_scoper(canvas); |
1344 canvas->sk_canvas()->scale(scale, scale); | 1508 canvas->sk_canvas()->scale(scale, scale); |
1345 canvas->TileImageInt(*fill_image, x_offset, y_offset, 0, 0, width(), | 1509 canvas->TileImageInt(*fill_image, x_offset, y_offset, 0, 0, width(), |
1346 height()); | 1510 height()); |
1347 } else { | 1511 } else { |
1348 paint.setColor( | 1512 paint.setColor( |
1349 is_active ? toolbar_color | 1513 is_active ? toolbar_color |
1350 : tp->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB)); | 1514 : tp->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB)); |
1351 canvas->DrawRect(gfx::ScaleToEnclosingRect(GetLocalBounds(), scale), | 1515 canvas->DrawRect(gfx::ScaleToEnclosingRect(GetLocalBounds(), scale), |
1352 paint); | 1516 paint); |
1353 } | 1517 } |
1354 if (draw_hover) { | 1518 if (draw_hover) { |
1355 hover_location.scale(SkFloatToScalar(scale)); | 1519 hover_location.scale(SkFloatToScalar(scale)); |
1356 DrawHighlight(canvas, hover_location, radius * scale, hover_color); | 1520 DrawHighlight(canvas, hover_location, radius * scale, hover_color); |
1357 } | 1521 } |
1358 } | 1522 } |
1359 | 1523 |
1360 // Draw the stroke. | 1524 // Draw the stroke. |
1361 SkPath stroke; | 1525 SkPath stroke; |
1362 GetBorderPath(scale, false, &stroke); | 1526 GetBorderPath(scale, size(), false, &stroke); |
1363 Op(stroke, fill, kDifference_SkPathOp, &stroke); | 1527 Op(stroke, fill, kDifference_SkPathOp, &stroke); |
1364 if (!is_active) { | 1528 if (!is_active) { |
1365 // Clip out the bottom line; this will be drawn for us by | 1529 // Clip out the bottom line; this will be drawn for us by |
1366 // TabStrip::PaintChildren(). | 1530 // TabStrip::PaintChildren(). |
1367 canvas->sk_canvas()->clipRect( | 1531 canvas->sk_canvas()->clipRect( |
1368 SkRect::MakeWH(width() * scale, height() * scale - 1)); | 1532 SkRect::MakeWH(width() * scale, height() * scale - 1)); |
1369 } | 1533 } |
1370 paint.setColor(controller_->GetToolbarTopSeparatorColor()); | 1534 paint.setColor(controller_->GetToolbarTopSeparatorColor()); |
1371 canvas->DrawPath(stroke, paint); | 1535 canvas->DrawPath(stroke, paint); |
1372 } else { | 1536 } else { |
1373 if (draw_hover) { | 1537 if (draw_hover) { |
1374 // Draw everything to a temporary canvas so we can extract an image for | 1538 // Draw everything to a temporary canvas so we can extract an image for |
1375 // use in masking the hover glow. | 1539 // use in masking the hover glow. |
1376 gfx::Canvas background_canvas(size(), canvas->image_scale(), false); | 1540 gfx::Canvas background_canvas(size(), canvas->image_scale(), false); |
1377 PaintTabFill(&background_canvas, fill_image, x_offset, y_offset, | 1541 PaintTabFill(&background_canvas, fill_image, x_offset, y_offset, size(), |
1378 is_active); | 1542 is_active); |
1379 gfx::ImageSkia background_image(background_canvas.ExtractImageRep()); | 1543 gfx::ImageSkia background_image(background_canvas.ExtractImageRep()); |
1380 canvas->DrawImageInt(background_image, 0, 0); | 1544 canvas->DrawImageInt(background_image, 0, 0); |
1381 | 1545 |
1382 gfx::Canvas hover_canvas(size(), canvas->image_scale(), false); | 1546 gfx::Canvas hover_canvas(size(), canvas->image_scale(), false); |
1383 DrawHighlight(&hover_canvas, hover_location, radius, hover_color); | 1547 DrawHighlight(&hover_canvas, hover_location, radius, hover_color); |
1384 gfx::ImageSkia result = gfx::ImageSkiaOperations::CreateMaskedImage( | 1548 gfx::ImageSkia result = gfx::ImageSkiaOperations::CreateMaskedImage( |
1385 gfx::ImageSkia(hover_canvas.ExtractImageRep()), background_image); | 1549 gfx::ImageSkia(hover_canvas.ExtractImageRep()), background_image); |
1386 canvas->DrawImageInt(result, 0, 0); | 1550 canvas->DrawImageInt(result, 0, 0); |
1387 } else { | 1551 } else { |
1388 PaintTabFill(canvas, fill_image, x_offset, y_offset, is_active); | 1552 PaintTabFill(canvas, fill_image, x_offset, y_offset, size(), is_active); |
1389 } | 1553 } |
1390 | 1554 |
1391 // Now draw the stroke, highlights, and shadows around the tab edge. | 1555 // Now draw the stroke, highlights, and shadows around the tab edge. |
1392 TabImages* stroke_images = is_active ? &active_images_ : &inactive_images_; | 1556 TabImages* stroke_images = |
| 1557 is_active ? &g_active_images : &g_inactive_images; |
1393 canvas->DrawImageInt(*stroke_images->image_l, 0, 0); | 1558 canvas->DrawImageInt(*stroke_images->image_l, 0, 0); |
1394 canvas->TileImageInt( | 1559 canvas->TileImageInt( |
1395 *stroke_images->image_c, stroke_images->l_width, 0, | 1560 *stroke_images->image_c, stroke_images->l_width, 0, |
1396 width() - stroke_images->l_width - stroke_images->r_width, height()); | 1561 width() - stroke_images->l_width - stroke_images->r_width, height()); |
1397 canvas->DrawImageInt(*stroke_images->image_r, | 1562 canvas->DrawImageInt(*stroke_images->image_r, |
1398 width() - stroke_images->r_width, 0); | 1563 width() - stroke_images->r_width, 0); |
1399 } | 1564 } |
1400 } | 1565 } |
1401 | 1566 |
1402 void Tab::PaintTabFill(gfx::Canvas* canvas, | |
1403 gfx::ImageSkia* fill_image, | |
1404 int x_offset, | |
1405 int y_offset, | |
1406 bool is_active) { | |
1407 const gfx::Insets tab_insets(GetLayoutInsets(TAB)); | |
1408 // If this isn't the foreground tab, don't draw over the toolbar, but do | |
1409 // include the 1 px divider stroke at the bottom. | |
1410 const int toolbar_overlap = is_active ? 0 : (tab_insets.bottom() - 1); | |
1411 | |
1412 // Draw left edge. | |
1413 gfx::ImageSkia tab_l = gfx::ImageSkiaOperations::CreateTiledImage( | |
1414 *fill_image, x_offset, y_offset, mask_images_.l_width, height()); | |
1415 gfx::ImageSkia theme_l = | |
1416 gfx::ImageSkiaOperations::CreateMaskedImage(tab_l, *mask_images_.image_l); | |
1417 canvas->DrawImageInt( | |
1418 theme_l, 0, 0, theme_l.width(), theme_l.height() - toolbar_overlap, 0, 0, | |
1419 theme_l.width(), theme_l.height() - toolbar_overlap, false); | |
1420 | |
1421 // Draw right edge. | |
1422 gfx::ImageSkia tab_r = gfx::ImageSkiaOperations::CreateTiledImage( | |
1423 *fill_image, x_offset + width() - mask_images_.r_width, y_offset, | |
1424 mask_images_.r_width, height()); | |
1425 gfx::ImageSkia theme_r = | |
1426 gfx::ImageSkiaOperations::CreateMaskedImage(tab_r, *mask_images_.image_r); | |
1427 canvas->DrawImageInt(theme_r, 0, 0, theme_r.width(), | |
1428 theme_r.height() - toolbar_overlap, | |
1429 width() - theme_r.width(), 0, theme_r.width(), | |
1430 theme_r.height() - toolbar_overlap, false); | |
1431 | |
1432 // Draw center. Instead of masking out the top portion we simply skip over it | |
1433 // by incrementing by the top padding, since it's a simple rectangle. | |
1434 canvas->TileImageInt(*fill_image, x_offset + mask_images_.l_width, | |
1435 y_offset + tab_insets.top(), mask_images_.l_width, | |
1436 tab_insets.top(), | |
1437 width() - mask_images_.l_width - mask_images_.r_width, | |
1438 height() - tab_insets.top() - toolbar_overlap); | |
1439 } | |
1440 | |
1441 void Tab::PaintPinnedTabTitleChangedIndicatorAndIcon( | 1567 void Tab::PaintPinnedTabTitleChangedIndicatorAndIcon( |
1442 gfx::Canvas* canvas, | 1568 gfx::Canvas* canvas, |
1443 const gfx::Rect& favicon_draw_bounds) { | 1569 const gfx::Rect& favicon_draw_bounds) { |
1444 // The pinned tab title changed indicator consists of two parts: | 1570 // The pinned tab title changed indicator consists of two parts: |
1445 // . a clear (totally transparent) part over the bottom right (or left in rtl) | 1571 // . a clear (totally transparent) part over the bottom right (or left in rtl) |
1446 // of the favicon. This is done by drawing the favicon to a canvas, then | 1572 // of the favicon. This is done by drawing the favicon to a canvas, then |
1447 // drawing the clear part on top of the favicon. | 1573 // drawing the clear part on top of the favicon. |
1448 // . a circle in the bottom right (or left in rtl) of the favicon. | 1574 // . a circle in the bottom right (or left in rtl) of the favicon. |
1449 if (!favicon_.isNull()) { | 1575 if (!favicon_.isNull()) { |
1450 const float kIndicatorCropRadius = 4.5; | 1576 const float kIndicatorCropRadius = 4.5; |
(...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1667 if (bounds.IsEmpty()) | 1793 if (bounds.IsEmpty()) |
1668 return; | 1794 return; |
1669 | 1795 |
1670 // Extends the area to the bottom when the crash animation is in progress. | 1796 // Extends the area to the bottom when the crash animation is in progress. |
1671 if (crash_icon_animation_->is_animating()) | 1797 if (crash_icon_animation_->is_animating()) |
1672 bounds.set_height(height() - bounds.y()); | 1798 bounds.set_height(height() - bounds.y()); |
1673 bounds.set_x(GetMirroredXForRect(bounds)); | 1799 bounds.set_x(GetMirroredXForRect(bounds)); |
1674 SchedulePaintInRect(bounds); | 1800 SchedulePaintInRect(bounds); |
1675 } | 1801 } |
1676 | 1802 |
1677 void Tab::GetFillPath(float scale, SkPath* fill) const { | |
1678 const float right = width() * scale; | |
1679 // The bottom of the tab needs to be pixel-aligned or else when we call | |
1680 // ClipPath with anti-aliasing enabled it can cause artifacts. | |
1681 const float bottom = std::ceil(height() * scale); | |
1682 const float unscaled_endcap_width = GetUnscaledEndcapWidth(); | |
1683 | |
1684 fill->moveTo(right - 1, bottom); | |
1685 fill->rCubicTo(-0.75 * scale, 0, -1.625 * scale, -0.5 * scale, -2 * scale, | |
1686 -1.5 * scale); | |
1687 fill->lineTo(right - 1 - (unscaled_endcap_width - 2) * scale, 2.5 * scale); | |
1688 // Prevent overdraw in the center near minimum width (only happens if | |
1689 // scale < 2). We could instead avoid this by increasing the tab inset | |
1690 // values, but that would shift all the content inward as well, unless we | |
1691 // then overlapped the content on the endcaps, by which point we'd have a | |
1692 // huge mess. | |
1693 const float scaled_endcap_width = 1 + unscaled_endcap_width * scale; | |
1694 const float overlap = scaled_endcap_width * 2 - right; | |
1695 const float offset = (overlap > 0) ? (overlap / 2) : 0; | |
1696 fill->rCubicTo(-0.375 * scale, -1 * scale, -1.25 * scale + offset, | |
1697 -1.5 * scale, -2 * scale + offset, -1.5 * scale); | |
1698 if (overlap < 0) | |
1699 fill->lineTo(scaled_endcap_width, scale); | |
1700 fill->rCubicTo(-0.75 * scale, 0, -1.625 * scale - offset, 0.5 * scale, | |
1701 -2 * scale - offset, 1.5 * scale); | |
1702 fill->lineTo(1 + 2 * scale, bottom - 1.5 * scale); | |
1703 fill->rCubicTo(-0.375 * scale, scale, -1.25 * scale, 1.5 * scale, -2 * scale, | |
1704 1.5 * scale); | |
1705 fill->close(); | |
1706 } | |
1707 | |
1708 void Tab::GetBorderPath(float scale, bool extend_to_top, SkPath* path) const { | |
1709 const float top = scale - 1; | |
1710 const float right = width() * scale; | |
1711 const float bottom = height() * scale; | |
1712 const float unscaled_endcap_width = GetUnscaledEndcapWidth(); | |
1713 | |
1714 path->moveTo(0, bottom); | |
1715 path->rLineTo(0, -1); | |
1716 path->rCubicTo(0.75 * scale, 0, 1.625 * scale, -0.5 * scale, 2 * scale, | |
1717 -1.5 * scale); | |
1718 path->lineTo((unscaled_endcap_width - 2) * scale, top + 1.5 * scale); | |
1719 if (extend_to_top) { | |
1720 // Create the vertical extension by extending the side diagonals until they | |
1721 // reach the top of the bounds. | |
1722 const float dy = 2.5 * scale - 1; | |
1723 const float dx = GetInverseDiagonalSlope() * dy; | |
1724 path->rLineTo(dx, -dy); | |
1725 path->lineTo(right - (unscaled_endcap_width - 2) * scale - dx, 0); | |
1726 path->rLineTo(dx, dy); | |
1727 } else { | |
1728 path->rCubicTo(0.375 * scale, -scale, 1.25 * scale, -1.5 * scale, 2 * scale, | |
1729 -1.5 * scale); | |
1730 path->lineTo(right - unscaled_endcap_width * scale, top); | |
1731 path->rCubicTo(0.75 * scale, 0, 1.625 * scale, 0.5 * scale, 2 * scale, | |
1732 1.5 * scale); | |
1733 } | |
1734 path->lineTo(right - 2 * scale, bottom - 1 - 1.5 * scale); | |
1735 path->rCubicTo(0.375 * scale, scale, 1.25 * scale, 1.5 * scale, 2 * scale, | |
1736 1.5 * scale); | |
1737 path->rLineTo(0, 1); | |
1738 path->close(); | |
1739 } | |
1740 | |
1741 gfx::Rect Tab::GetImmersiveBarRect() const { | 1803 gfx::Rect Tab::GetImmersiveBarRect() const { |
1742 // The main bar is as wide as the normal tab's horizontal top line. | 1804 // The main bar is as wide as the normal tab's horizontal top line. |
1743 // This top line of the tab extends a few pixels left and right of the | 1805 // This top line of the tab extends a few pixels left and right of the |
1744 // center image due to pixels in the rounded corner images. | 1806 // center image due to pixels in the rounded corner images. |
1745 const int kBarPadding = 1; | 1807 const int kBarPadding = 1; |
1746 int main_bar_left = active_images_.l_width - kBarPadding; | 1808 int main_bar_left = g_active_images.l_width - kBarPadding; |
1747 int main_bar_right = width() - active_images_.r_width + kBarPadding; | 1809 int main_bar_right = width() - g_active_images.r_width + kBarPadding; |
1748 return gfx::Rect( | 1810 return gfx::Rect( |
1749 main_bar_left, 0, main_bar_right - main_bar_left, kImmersiveBarHeight); | 1811 main_bar_left, 0, main_bar_right - main_bar_left, kImmersiveBarHeight); |
1750 } | 1812 } |
1751 | |
1752 //////////////////////////////////////////////////////////////////////////////// | |
1753 // Tab, private static: | |
1754 | |
1755 // static | |
1756 void Tab::InitTabResources() { | |
1757 static bool initialized = false; | |
1758 if (initialized) | |
1759 return; | |
1760 | |
1761 initialized = true; | |
1762 image_cache_ = new ImageCache(); | |
1763 | |
1764 // Load the tab images once now, and maybe again later if the theme changes. | |
1765 LoadTabImages(); | |
1766 } | |
1767 | |
1768 // static | |
1769 void Tab::LoadTabImages() { | |
1770 image_cache_->clear(); | |
1771 | |
1772 // We're not letting people override tab images just yet. | |
1773 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | |
1774 | |
1775 active_images_.image_l = rb.GetImageSkiaNamed(IDR_TAB_ACTIVE_LEFT); | |
1776 active_images_.image_c = rb.GetImageSkiaNamed(IDR_TAB_ACTIVE_CENTER); | |
1777 active_images_.image_r = rb.GetImageSkiaNamed(IDR_TAB_ACTIVE_RIGHT); | |
1778 active_images_.l_width = active_images_.image_l->width(); | |
1779 active_images_.r_width = active_images_.image_r->width(); | |
1780 | |
1781 inactive_images_.image_l = rb.GetImageSkiaNamed(IDR_TAB_INACTIVE_LEFT); | |
1782 inactive_images_.image_c = rb.GetImageSkiaNamed(IDR_TAB_INACTIVE_CENTER); | |
1783 inactive_images_.image_r = rb.GetImageSkiaNamed(IDR_TAB_INACTIVE_RIGHT); | |
1784 inactive_images_.l_width = inactive_images_.image_l->width(); | |
1785 inactive_images_.r_width = inactive_images_.image_r->width(); | |
1786 | |
1787 mask_images_.image_l = rb.GetImageSkiaNamed(IDR_TAB_ALPHA_LEFT); | |
1788 mask_images_.image_r = rb.GetImageSkiaNamed(IDR_TAB_ALPHA_RIGHT); | |
1789 mask_images_.l_width = mask_images_.image_l->width(); | |
1790 mask_images_.r_width = mask_images_.image_r->width(); | |
1791 } | |
OLD | NEW |