| 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 16 matching lines...) Expand all Loading... |
| 27 #include "chrome/grit/generated_resources.h" | 27 #include "chrome/grit/generated_resources.h" |
| 28 #include "chrome/grit/theme_resources.h" | 28 #include "chrome/grit/theme_resources.h" |
| 29 #include "components/grit/components_scaled_resources.h" | 29 #include "components/grit/components_scaled_resources.h" |
| 30 #include "components/strings/grit/components_strings.h" | 30 #include "components/strings/grit/components_strings.h" |
| 31 #include "content/public/browser/user_metrics.h" | 31 #include "content/public/browser/user_metrics.h" |
| 32 #include "content/public/common/url_constants.h" | 32 #include "content/public/common/url_constants.h" |
| 33 #include "third_party/skia/include/effects/SkGradientShader.h" | 33 #include "third_party/skia/include/effects/SkGradientShader.h" |
| 34 #include "third_party/skia/include/pathops/SkPathOps.h" | 34 #include "third_party/skia/include/pathops/SkPathOps.h" |
| 35 #include "ui/accessibility/ax_view_state.h" | 35 #include "ui/accessibility/ax_view_state.h" |
| 36 #include "ui/base/l10n/l10n_util.h" | 36 #include "ui/base/l10n/l10n_util.h" |
| 37 #include "ui/base/material_design/material_design_controller.h" | |
| 38 #include "ui/base/models/list_selection_model.h" | 37 #include "ui/base/models/list_selection_model.h" |
| 39 #include "ui/base/resource/resource_bundle.h" | 38 #include "ui/base/resource/resource_bundle.h" |
| 40 #include "ui/base/theme_provider.h" | 39 #include "ui/base/theme_provider.h" |
| 41 #include "ui/gfx/animation/animation_container.h" | 40 #include "ui/gfx/animation/animation_container.h" |
| 42 #include "ui/gfx/animation/throb_animation.h" | 41 #include "ui/gfx/animation/throb_animation.h" |
| 43 #include "ui/gfx/canvas.h" | 42 #include "ui/gfx/canvas.h" |
| 44 #include "ui/gfx/color_analysis.h" | 43 #include "ui/gfx/color_analysis.h" |
| 45 #include "ui/gfx/favicon_size.h" | 44 #include "ui/gfx/favicon_size.h" |
| 46 #include "ui/gfx/geometry/rect_conversions.h" | 45 #include "ui/gfx/geometry/rect_conversions.h" |
| 47 #include "ui/gfx/image/image_skia_operations.h" | 46 #include "ui/gfx/image/image_skia_operations.h" |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 88 const double kSelectedTabOpacity = 0.3; | 87 const double kSelectedTabOpacity = 0.3; |
| 89 | 88 |
| 90 // Inactive selected tabs have their throb value scaled by this. | 89 // Inactive selected tabs have their throb value scaled by this. |
| 91 const double kSelectedTabThrobScale = 0.95 - kSelectedTabOpacity; | 90 const double kSelectedTabThrobScale = 0.95 - kSelectedTabOpacity; |
| 92 | 91 |
| 93 // Max number of images to cache. This has to be at least two since rounding | 92 // Max number of images to cache. This has to be at least two since rounding |
| 94 // errors may lead to tabs in the same tabstrip having different sizes. | 93 // errors may lead to tabs in the same tabstrip having different sizes. |
| 95 // 8 = normal/incognito, active/inactive, 2 sizes within tabstrip. | 94 // 8 = normal/incognito, active/inactive, 2 sizes within tabstrip. |
| 96 const size_t kMaxImageCacheSize = 8; | 95 const size_t kMaxImageCacheSize = 8; |
| 97 | 96 |
| 98 // Height of the miniature tab strip in immersive mode. | |
| 99 const int kImmersiveTabHeight = 3; | |
| 100 | |
| 101 // Height of the small tab indicator rectangles in immersive mode. | |
| 102 const int kImmersiveBarHeight = 2; | |
| 103 | |
| 104 // Color for active and inactive tabs in the immersive mode light strip. These | |
| 105 // should be a little brighter than the color of the normal art assets for tabs, | |
| 106 // which for active tabs is 230, 230, 230 and for inactive is 184, 184, 184. | |
| 107 const SkColor kImmersiveActiveTabColor = SkColorSetRGB(235, 235, 235); | |
| 108 const SkColor kImmersiveInactiveTabColor = SkColorSetRGB(190, 190, 190); | |
| 109 | |
| 110 // The minimum opacity (out of 1) when a tab (either active or inactive) is | |
| 111 // throbbing in the immersive mode light strip. | |
| 112 const double kImmersiveTabMinThrobOpacity = 0.66; | |
| 113 | |
| 114 // Number of steps in the immersive mode loading animation. | |
| 115 const int kImmersiveLoadingStepCount = 32; | |
| 116 | |
| 117 const char kTabCloseButtonName[] = "TabCloseButton"; | 97 const char kTabCloseButtonName[] = "TabCloseButton"; |
| 118 | 98 |
| 119 //////////////////////////////////////////////////////////////////////////////// | 99 //////////////////////////////////////////////////////////////////////////////// |
| 120 // ImageCacheEntryMetadata | 100 // ImageCacheEntryMetadata |
| 121 // | 101 // |
| 122 // All metadata necessary to uniquely identify a cached image. | 102 // All metadata necessary to uniquely identify a cached image. |
| 123 struct ImageCacheEntryMetadata { | 103 struct ImageCacheEntryMetadata { |
| 124 ImageCacheEntryMetadata(int resource_id, | 104 ImageCacheEntryMetadata(SkColor fill_color, |
| 125 SkColor fill_color, | |
| 126 SkColor stroke_color, | 105 SkColor stroke_color, |
| 127 bool use_fill_and_stroke_images, | 106 bool use_fill_and_stroke_images, |
| 128 float scale_factor, | 107 float scale_factor, |
| 129 const gfx::Size& size); | 108 const gfx::Size& size); |
| 130 | 109 |
| 131 ~ImageCacheEntryMetadata(); | 110 ~ImageCacheEntryMetadata(); |
| 132 | 111 |
| 133 bool operator==(const ImageCacheEntryMetadata& rhs) const; | 112 bool operator==(const ImageCacheEntryMetadata& rhs) const; |
| 134 | 113 |
| 135 int resource_id; // Only needed by pre-MD | 114 SkColor fill_color; |
| 136 SkColor fill_color; // Both colors only needed by MD | |
| 137 SkColor stroke_color; | 115 SkColor stroke_color; |
| 138 bool use_fill_and_stroke_images; | 116 bool use_fill_and_stroke_images; |
| 139 float scale_factor; | 117 float scale_factor; |
| 140 gfx::Size size; | 118 gfx::Size size; |
| 141 }; | 119 }; |
| 142 | 120 |
| 143 ImageCacheEntryMetadata::ImageCacheEntryMetadata( | 121 ImageCacheEntryMetadata::ImageCacheEntryMetadata( |
| 144 int resource_id, | |
| 145 SkColor fill_color, | 122 SkColor fill_color, |
| 146 SkColor stroke_color, | 123 SkColor stroke_color, |
| 147 bool use_fill_and_stroke_images, | 124 bool use_fill_and_stroke_images, |
| 148 float scale_factor, | 125 float scale_factor, |
| 149 const gfx::Size& size) | 126 const gfx::Size& size) |
| 150 : resource_id(resource_id), | 127 : fill_color(fill_color), |
| 151 fill_color(fill_color), | |
| 152 stroke_color(stroke_color), | 128 stroke_color(stroke_color), |
| 153 use_fill_and_stroke_images(use_fill_and_stroke_images), | 129 use_fill_and_stroke_images(use_fill_and_stroke_images), |
| 154 scale_factor(scale_factor), | 130 scale_factor(scale_factor), |
| 155 size(size) { | 131 size(size) {} |
| 156 // Some fields are only relevant for pre-MD vs. MD. Erase the irrelevant ones | |
| 157 // so they don't cause incorrect cache misses. | |
| 158 // TODO(pkasting): Remove |resource_id| field when non-MD code is deleted. | |
| 159 if (ui::MaterialDesignController::IsModeMaterial()) | |
| 160 resource_id = 0; | |
| 161 else | |
| 162 fill_color = stroke_color = SK_ColorTRANSPARENT; | |
| 163 } | |
| 164 | 132 |
| 165 ImageCacheEntryMetadata::~ImageCacheEntryMetadata() {} | 133 ImageCacheEntryMetadata::~ImageCacheEntryMetadata() {} |
| 166 | 134 |
| 167 bool ImageCacheEntryMetadata::operator==( | 135 bool ImageCacheEntryMetadata::operator==( |
| 168 const ImageCacheEntryMetadata& rhs) const { | 136 const ImageCacheEntryMetadata& rhs) const { |
| 169 return resource_id == rhs.resource_id && fill_color == rhs.fill_color && | 137 return fill_color == rhs.fill_color && stroke_color == rhs.stroke_color && |
| 170 stroke_color == rhs.stroke_color && | |
| 171 use_fill_and_stroke_images == rhs.use_fill_and_stroke_images && | 138 use_fill_and_stroke_images == rhs.use_fill_and_stroke_images && |
| 172 scale_factor == rhs.scale_factor && size == rhs.size; | 139 scale_factor == rhs.scale_factor && size == rhs.size; |
| 173 } | 140 } |
| 174 | 141 |
| 175 //////////////////////////////////////////////////////////////////////////////// | 142 //////////////////////////////////////////////////////////////////////////////// |
| 176 // ImageCacheEntry and cache management | 143 // ImageCacheEntry and cache management |
| 177 // | 144 // |
| 178 // A cached image and the metadata used to generate it. | 145 // A cached image and the metadata used to generate it. |
| 179 struct ImageCacheEntry { | 146 struct ImageCacheEntry { |
| 180 ImageCacheEntry(const ImageCacheEntryMetadata& metadata, | 147 ImageCacheEntry(const ImageCacheEntryMetadata& metadata, |
| (...skipping 12 matching lines...) Expand all Loading... |
| 193 : metadata(metadata), fill_image(fill_image), stroke_image(stroke_image) {} | 160 : metadata(metadata), fill_image(fill_image), stroke_image(stroke_image) {} |
| 194 | 161 |
| 195 ImageCacheEntry::~ImageCacheEntry() {} | 162 ImageCacheEntry::~ImageCacheEntry() {} |
| 196 | 163 |
| 197 typedef std::list<ImageCacheEntry> ImageCache; | 164 typedef std::list<ImageCacheEntry> ImageCache; |
| 198 | 165 |
| 199 // As the majority of the tabs are inactive, and painting tabs is slowish, | 166 // As the majority of the tabs are inactive, and painting tabs is slowish, |
| 200 // we cache a handful of the inactive tab backgrounds here. | 167 // we cache a handful of the inactive tab backgrounds here. |
| 201 static ImageCache* g_image_cache = nullptr; | 168 static ImageCache* g_image_cache = nullptr; |
| 202 | 169 |
| 203 struct TabImages { | |
| 204 gfx::ImageSkia* image_l; | |
| 205 gfx::ImageSkia* image_c; | |
| 206 gfx::ImageSkia* image_r; | |
| 207 int l_width; | |
| 208 int r_width; | |
| 209 }; | |
| 210 static TabImages g_active_images = {0}; | |
| 211 static TabImages g_inactive_images = {0}; | |
| 212 static TabImages g_mask_images = {0}; | |
| 213 | |
| 214 // Loads the images to be used for the tab background. | |
| 215 void LoadTabImages() { | |
| 216 g_image_cache->clear(); | |
| 217 | |
| 218 // We're not letting people override tab images just yet. | |
| 219 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | |
| 220 | |
| 221 g_active_images.image_l = rb.GetImageSkiaNamed(IDR_TAB_ACTIVE_LEFT); | |
| 222 g_active_images.image_c = rb.GetImageSkiaNamed(IDR_TAB_ACTIVE_CENTER); | |
| 223 g_active_images.image_r = rb.GetImageSkiaNamed(IDR_TAB_ACTIVE_RIGHT); | |
| 224 g_active_images.l_width = g_active_images.image_l->width(); | |
| 225 g_active_images.r_width = g_active_images.image_r->width(); | |
| 226 | |
| 227 g_inactive_images.image_l = rb.GetImageSkiaNamed(IDR_TAB_INACTIVE_LEFT); | |
| 228 g_inactive_images.image_c = rb.GetImageSkiaNamed(IDR_TAB_INACTIVE_CENTER); | |
| 229 g_inactive_images.image_r = rb.GetImageSkiaNamed(IDR_TAB_INACTIVE_RIGHT); | |
| 230 g_inactive_images.l_width = g_inactive_images.image_l->width(); | |
| 231 g_inactive_images.r_width = g_inactive_images.image_r->width(); | |
| 232 | |
| 233 g_mask_images.image_l = rb.GetImageSkiaNamed(IDR_TAB_ALPHA_LEFT); | |
| 234 g_mask_images.image_r = rb.GetImageSkiaNamed(IDR_TAB_ALPHA_RIGHT); | |
| 235 g_mask_images.l_width = g_mask_images.image_l->width(); | |
| 236 g_mask_images.r_width = g_mask_images.image_r->width(); | |
| 237 } | |
| 238 | |
| 239 // Performs a one-time initialization of static resources such as tab images. | 170 // Performs a one-time initialization of static resources such as tab images. |
| 240 void InitTabResources() { | 171 void InitTabResources() { |
| 241 static bool initialized = false; | 172 static bool initialized = false; |
| 242 if (initialized) | 173 if (initialized) |
| 243 return; | 174 return; |
| 244 | 175 |
| 245 initialized = true; | 176 initialized = true; |
| 246 g_image_cache = new ImageCache(); | 177 g_image_cache = new ImageCache(); |
| 247 | |
| 248 // Load the tab images once now, and maybe again later if the theme changes. | |
| 249 LoadTabImages(); | |
| 250 } | 178 } |
| 251 | 179 |
| 252 //////////////////////////////////////////////////////////////////////////////// | 180 //////////////////////////////////////////////////////////////////////////////// |
| 253 // Drawing and utility functions | 181 // Drawing and utility functions |
| 254 | 182 |
| 255 // Returns the width of the tab endcap at scale 1. More precisely, this is the | 183 // Returns the width of the tab endcap at scale 1. More precisely, this is the |
| 256 // width of the curve making up either the outer or inner edge of the stroke; | 184 // width of the curve making up either the outer or inner edge of the stroke; |
| 257 // since these two curves are horizontally offset by 1 px (regardless of scale), | 185 // since these two curves are horizontally offset by 1 px (regardless of scale), |
| 258 // the total width of the endcap from tab outer edge to the inside end of the | 186 // the total width of the endcap from tab outer edge to the inside end of the |
| 259 // stroke inner edge is (GetUnscaledEndcapWidth() * scale) + 1. | 187 // stroke inner edge is (GetUnscaledEndcapWidth() * scale) + 1. |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 321 | 249 |
| 322 // Returns a path corresponding to the tab's outer border for a given tab |size| | 250 // Returns a path corresponding to the tab's outer border for a given tab |size| |
| 323 // and |scale|. If |unscale_at_end| is true, this path will be normalized to a | 251 // and |scale|. If |unscale_at_end| is true, this path will be normalized to a |
| 324 // 1x scale by scaling by 1/scale before returning. If |extend_to_top| is true, | 252 // 1x scale by scaling by 1/scale before returning. If |extend_to_top| is true, |
| 325 // the path is extended vertically to the top of the tab bounds. The caller | 253 // the path is extended vertically to the top of the tab bounds. The caller |
| 326 // uses this for Fitts' Law purposes in maximized/fullscreen mode. | 254 // uses this for Fitts' Law purposes in maximized/fullscreen mode. |
| 327 gfx::Path GetBorderPath(float scale, | 255 gfx::Path GetBorderPath(float scale, |
| 328 bool unscale_at_end, | 256 bool unscale_at_end, |
| 329 bool extend_to_top, | 257 bool extend_to_top, |
| 330 const gfx::Size& size) { | 258 const gfx::Size& size) { |
| 259 const float top = scale - 1; |
| 260 const float right = size.width() * scale; |
| 261 const float bottom = size.height() * scale; |
| 262 const float unscaled_endcap_width = GetUnscaledEndcapWidth(); |
| 263 |
| 331 gfx::Path path; | 264 gfx::Path path; |
| 332 if (ui::MaterialDesignController::IsModeMaterial()) { | 265 path.moveTo(0, bottom); |
| 333 const float top = scale - 1; | 266 path.rLineTo(0, -1); |
| 334 const float right = size.width() * scale; | 267 path.rCubicTo(0.75 * scale, 0, 1.625 * scale, -0.5 * scale, 2 * scale, |
| 335 const float bottom = size.height() * scale; | 268 -1.5 * scale); |
| 336 const float unscaled_endcap_width = GetUnscaledEndcapWidth(); | 269 path.lineTo((unscaled_endcap_width - 2) * scale, top + 1.5 * scale); |
| 270 if (extend_to_top) { |
| 271 // Create the vertical extension by extending the side diagonals until |
| 272 // they reach the top of the bounds. |
| 273 const float dy = 2.5 * scale - 1; |
| 274 const float dx = Tab::GetInverseDiagonalSlope() * dy; |
| 275 path.rLineTo(dx, -dy); |
| 276 path.lineTo(right - (unscaled_endcap_width - 2) * scale - dx, 0); |
| 277 path.rLineTo(dx, dy); |
| 278 } else { |
| 279 path.rCubicTo(0.375 * scale, -scale, 1.25 * scale, -1.5 * scale, 2 * scale, |
| 280 -1.5 * scale); |
| 281 path.lineTo(right - unscaled_endcap_width * scale, top); |
| 282 path.rCubicTo(0.75 * scale, 0, 1.625 * scale, 0.5 * scale, 2 * scale, |
| 283 1.5 * scale); |
| 284 } |
| 285 path.lineTo(right - 2 * scale, bottom - 1 - 1.5 * scale); |
| 286 path.rCubicTo(0.375 * scale, scale, 1.25 * scale, 1.5 * scale, 2 * scale, |
| 287 1.5 * scale); |
| 288 path.rLineTo(0, 1); |
| 289 path.close(); |
| 337 | 290 |
| 338 path.moveTo(0, bottom); | 291 if (unscale_at_end && (scale != 1)) |
| 339 path.rLineTo(0, -1); | 292 path.transform(SkMatrix::MakeScale(1.f / scale)); |
| 340 path.rCubicTo(0.75 * scale, 0, 1.625 * scale, -0.5 * scale, 2 * scale, | |
| 341 -1.5 * scale); | |
| 342 path.lineTo((unscaled_endcap_width - 2) * scale, top + 1.5 * scale); | |
| 343 if (extend_to_top) { | |
| 344 // Create the vertical extension by extending the side diagonals until | |
| 345 // they reach the top of the bounds. | |
| 346 const float dy = 2.5 * scale - 1; | |
| 347 const float dx = Tab::GetInverseDiagonalSlope() * dy; | |
| 348 path.rLineTo(dx, -dy); | |
| 349 path.lineTo(right - (unscaled_endcap_width - 2) * scale - dx, 0); | |
| 350 path.rLineTo(dx, dy); | |
| 351 } else { | |
| 352 path.rCubicTo(0.375 * scale, -scale, 1.25 * scale, -1.5 * scale, | |
| 353 2 * scale, -1.5 * scale); | |
| 354 path.lineTo(right - unscaled_endcap_width * scale, top); | |
| 355 path.rCubicTo(0.75 * scale, 0, 1.625 * scale, 0.5 * scale, 2 * scale, | |
| 356 1.5 * scale); | |
| 357 } | |
| 358 path.lineTo(right - 2 * scale, bottom - 1 - 1.5 * scale); | |
| 359 path.rCubicTo(0.375 * scale, scale, 1.25 * scale, 1.5 * scale, 2 * scale, | |
| 360 1.5 * scale); | |
| 361 path.rLineTo(0, 1); | |
| 362 path.close(); | |
| 363 | |
| 364 if (unscale_at_end && (scale != 1)) | |
| 365 path.transform(SkMatrix::MakeScale(1.f / scale)); | |
| 366 } else { | |
| 367 // Hit mask constants. | |
| 368 const SkScalar kTabCapWidth = 15; | |
| 369 const SkScalar kTabTopCurveWidth = 4; | |
| 370 const SkScalar kTabBottomCurveWidth = 3; | |
| 371 #if defined(OS_MACOSX) | |
| 372 // Mac's Cocoa UI doesn't have shadows. | |
| 373 const SkScalar kTabInset = 0; | |
| 374 #elif defined(TOOLKIT_VIEWS) | |
| 375 // The views browser UI has shadows in the left, right and top parts of the | |
| 376 // tab. | |
| 377 const SkScalar kTabInset = 6; | |
| 378 #endif | |
| 379 | |
| 380 SkScalar left = kTabInset; | |
| 381 SkScalar top = SkIntToScalar(0); | |
| 382 SkScalar right = SkIntToScalar(size.width()) - kTabInset; | |
| 383 SkScalar bottom = SkIntToScalar(size.height()); | |
| 384 | |
| 385 // Start in the lower-left corner. | |
| 386 path.moveTo(left, bottom); | |
| 387 | |
| 388 // Left end cap. | |
| 389 path.lineTo(left + kTabBottomCurveWidth, bottom - kTabBottomCurveWidth); | |
| 390 path.lineTo(left + kTabCapWidth - kTabTopCurveWidth, | |
| 391 top + kTabTopCurveWidth); | |
| 392 path.lineTo(left + kTabCapWidth, top); | |
| 393 | |
| 394 // Connect to the right cap. | |
| 395 path.lineTo(right - kTabCapWidth, top); | |
| 396 | |
| 397 // Right end cap. | |
| 398 path.lineTo(right - kTabCapWidth + kTabTopCurveWidth, | |
| 399 top + kTabTopCurveWidth); | |
| 400 path.lineTo(right - kTabBottomCurveWidth, bottom - kTabBottomCurveWidth); | |
| 401 path.lineTo(right, bottom); | |
| 402 | |
| 403 // Close out the path. | |
| 404 path.lineTo(left, bottom); | |
| 405 path.close(); | |
| 406 | |
| 407 // Unscaling is not necessary since we never scaled up to begin with. | |
| 408 } | |
| 409 | 293 |
| 410 return path; | 294 return path; |
| 411 } | 295 } |
| 412 | 296 |
| 413 void PaintTabFill(gfx::Canvas* canvas, | |
| 414 const gfx::ImageSkia& fill_image, | |
| 415 gfx::Rect rect, | |
| 416 bool is_active) { | |
| 417 const gfx::Insets tab_insets(GetLayoutInsets(TAB)); | |
| 418 // If this isn't the foreground tab, don't draw over the toolbar, but do | |
| 419 // include the 1 px divider stroke at the bottom. | |
| 420 const int toolbar_overlap = is_active ? 0 : (tab_insets.bottom() - 1); | |
| 421 | |
| 422 // Draw left edge. | |
| 423 gfx::ImageSkia tab_l = gfx::ImageSkiaOperations::CreateTiledImage( | |
| 424 fill_image, rect.x(), rect.y(), g_mask_images.l_width, rect.height()); | |
| 425 gfx::ImageSkia theme_l = gfx::ImageSkiaOperations::CreateMaskedImage( | |
| 426 tab_l, *g_mask_images.image_l); | |
| 427 canvas->DrawImageInt( | |
| 428 theme_l, 0, 0, theme_l.width(), theme_l.height() - toolbar_overlap, 0, 0, | |
| 429 theme_l.width(), theme_l.height() - toolbar_overlap, false); | |
| 430 | |
| 431 // Draw right edge. | |
| 432 gfx::ImageSkia tab_r = gfx::ImageSkiaOperations::CreateTiledImage( | |
| 433 fill_image, rect.right() - g_mask_images.r_width, rect.y(), | |
| 434 g_mask_images.r_width, rect.height()); | |
| 435 gfx::ImageSkia theme_r = gfx::ImageSkiaOperations::CreateMaskedImage( | |
| 436 tab_r, *g_mask_images.image_r); | |
| 437 canvas->DrawImageInt(theme_r, 0, 0, theme_r.width(), | |
| 438 theme_r.height() - toolbar_overlap, | |
| 439 rect.width() - theme_r.width(), 0, theme_r.width(), | |
| 440 theme_r.height() - toolbar_overlap, false); | |
| 441 | |
| 442 // Draw center. Instead of masking out the top portion we simply skip over it | |
| 443 // by incrementing by the top padding, since it's a simple rectangle. | |
| 444 rect.Inset(g_mask_images.l_width, tab_insets.top(), g_mask_images.r_width, | |
| 445 toolbar_overlap); | |
| 446 canvas->TileImageInt(fill_image, rect.x(), rect.y(), g_mask_images.l_width, | |
| 447 tab_insets.top(), rect.width(), rect.height()); | |
| 448 } | |
| 449 | |
| 450 } // namespace | 297 } // namespace |
| 451 | 298 |
| 452 //////////////////////////////////////////////////////////////////////////////// | 299 //////////////////////////////////////////////////////////////////////////////// |
| 453 // FaviconCrashAnimation | 300 // FaviconCrashAnimation |
| 454 // | 301 // |
| 455 // A custom animation subclass to manage the favicon crash animation. | 302 // A custom animation subclass to manage the favicon crash animation. |
| 456 class Tab::FaviconCrashAnimation : public gfx::LinearAnimation, | 303 class Tab::FaviconCrashAnimation : public gfx::LinearAnimation, |
| 457 public gfx::AnimationDelegate { | 304 public gfx::AnimationDelegate { |
| 458 public: | 305 public: |
| 459 explicit FaviconCrashAnimation(Tab* target) | 306 explicit FaviconCrashAnimation(Tab* target) |
| (...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 660 | 507 |
| 661 // static | 508 // static |
| 662 const char Tab::kViewClassName[] = "Tab"; | 509 const char Tab::kViewClassName[] = "Tab"; |
| 663 | 510 |
| 664 Tab::Tab(TabController* controller, gfx::AnimationContainer* container) | 511 Tab::Tab(TabController* controller, gfx::AnimationContainer* container) |
| 665 : controller_(controller), | 512 : controller_(controller), |
| 666 closing_(false), | 513 closing_(false), |
| 667 dragging_(false), | 514 dragging_(false), |
| 668 detached_(false), | 515 detached_(false), |
| 669 favicon_hiding_offset_(0), | 516 favicon_hiding_offset_(0), |
| 670 immersive_loading_step_(0), | |
| 671 should_display_crashed_favicon_(false), | 517 should_display_crashed_favicon_(false), |
| 672 pulse_animation_(new gfx::ThrobAnimation(this)), | 518 pulse_animation_(new gfx::ThrobAnimation(this)), |
| 673 crash_icon_animation_(new FaviconCrashAnimation(this)), | 519 crash_icon_animation_(new FaviconCrashAnimation(this)), |
| 674 animation_container_(container), | 520 animation_container_(container), |
| 675 throbber_(nullptr), | 521 throbber_(nullptr), |
| 676 alert_indicator_button_(nullptr), | 522 alert_indicator_button_(nullptr), |
| 677 close_button_(nullptr), | 523 close_button_(nullptr), |
| 678 title_(new views::Label()), | 524 title_(new views::Label()), |
| 679 tab_activated_with_last_tap_down_(false), | 525 tab_activated_with_last_tap_down_(false), |
| 680 hover_controller_(this), | 526 hover_controller_(this), |
| (...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 871 return kTouchWidth; | 717 return kTouchWidth; |
| 872 } | 718 } |
| 873 | 719 |
| 874 // static | 720 // static |
| 875 int Tab::GetPinnedWidth() { | 721 int Tab::GetPinnedWidth() { |
| 876 return GetMinimumInactiveSize().width() + | 722 return GetMinimumInactiveSize().width() + |
| 877 GetLayoutConstant(TAB_PINNED_CONTENT_WIDTH); | 723 GetLayoutConstant(TAB_PINNED_CONTENT_WIDTH); |
| 878 } | 724 } |
| 879 | 725 |
| 880 // static | 726 // static |
| 881 int Tab::GetImmersiveHeight() { | |
| 882 return kImmersiveTabHeight; | |
| 883 } | |
| 884 | |
| 885 // static | |
| 886 float Tab::GetInverseDiagonalSlope() { | 727 float Tab::GetInverseDiagonalSlope() { |
| 887 // This is computed from the border path as follows: | 728 // This is computed from the border path as follows: |
| 888 // * The unscaled endcap width is enough for the whole stroke outer curve, | 729 // * The unscaled endcap width is enough for the whole stroke outer curve, |
| 889 // i.e. the side diagonal plus the curves on both its ends. | 730 // i.e. the side diagonal plus the curves on both its ends. |
| 890 // * The bottom and top curve are each (2 * scale) px wide, so the diagonal is | 731 // * The bottom and top curve are each (2 * scale) px wide, so the diagonal is |
| 891 // (unscaled endcap width - 2 - 2) * scale px wide. | 732 // (unscaled endcap width - 2 - 2) * scale px wide. |
| 892 // * The bottom and top curve are each 1.5 px high. Additionally, there is an | 733 // * The bottom and top curve are each 1.5 px high. Additionally, there is an |
| 893 // extra 1 px below the bottom curve and (scale - 1) px above the top curve, | 734 // extra 1 px below the bottom curve and (scale - 1) px above the top curve, |
| 894 // so the diagonal is ((height - 1.5 - 1.5) * scale - 1 - (scale - 1)) px | 735 // so the diagonal is ((height - 1.5 - 1.5) * scale - 1 - (scale - 1)) px |
| 895 // high. | 736 // high. |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 948 if (!closing()) | 789 if (!closing()) |
| 949 controller_->ShowContextMenuForTab(this, point, source_type); | 790 controller_->ShowContextMenuForTab(this, point, source_type); |
| 950 } | 791 } |
| 951 | 792 |
| 952 //////////////////////////////////////////////////////////////////////////////// | 793 //////////////////////////////////////////////////////////////////////////////// |
| 953 // Tab, views::MaskedTargeterDelegate overrides: | 794 // Tab, views::MaskedTargeterDelegate overrides: |
| 954 | 795 |
| 955 bool Tab::GetHitTestMask(gfx::Path* mask) const { | 796 bool Tab::GetHitTestMask(gfx::Path* mask) const { |
| 956 // When the window is maximized we don't want to shave off the edges or top | 797 // When the window is maximized we don't want to shave off the edges or top |
| 957 // shadow of the tab, such that the user can click anywhere along the top | 798 // shadow of the tab, such that the user can click anywhere along the top |
| 958 // edge of the screen to select a tab. Ditto for immersive fullscreen. | 799 // edge of the screen to select a tab. |
| 959 const views::Widget* widget = GetWidget(); | 800 const views::Widget* widget = GetWidget(); |
| 960 *mask = GetBorderPath( | 801 *mask = GetBorderPath( |
| 961 GetWidget()->GetCompositor()->device_scale_factor(), true, | 802 GetWidget()->GetCompositor()->device_scale_factor(), true, |
| 962 widget && (widget->IsMaximized() || widget->IsFullscreen()), size()); | 803 widget && (widget->IsMaximized() || widget->IsFullscreen()), size()); |
| 963 return true; | 804 return true; |
| 964 } | 805 } |
| 965 | 806 |
| 966 //////////////////////////////////////////////////////////////////////////////// | 807 //////////////////////////////////////////////////////////////////////////////// |
| 967 // Tab, views::View overrides: | 808 // Tab, views::View overrides: |
| 968 | 809 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 979 // only happen during animations). | 820 // only happen during animations). |
| 980 if (width() < GetMinimumInactiveSize().width() && !data().pinned) | 821 if (width() < GetMinimumInactiveSize().width() && !data().pinned) |
| 981 return; | 822 return; |
| 982 | 823 |
| 983 gfx::Path clip; | 824 gfx::Path clip; |
| 984 if (!controller_->ShouldPaintTab( | 825 if (!controller_->ShouldPaintTab( |
| 985 this, base::Bind(&GetBorderPath, canvas->image_scale(), true, false), | 826 this, base::Bind(&GetBorderPath, canvas->image_scale(), true, false), |
| 986 &clip)) | 827 &clip)) |
| 987 return; | 828 return; |
| 988 | 829 |
| 989 if (controller_->IsImmersiveStyle()) | 830 PaintTab(canvas, clip); |
| 990 PaintImmersiveTab(canvas); | |
| 991 else | |
| 992 PaintTab(canvas, clip); | |
| 993 } | 831 } |
| 994 | 832 |
| 995 void Tab::Layout() { | 833 void Tab::Layout() { |
| 996 const gfx::Rect lb = GetContentsBounds(); | 834 const gfx::Rect lb = GetContentsBounds(); |
| 997 showing_icon_ = ShouldShowIcon(); | 835 showing_icon_ = ShouldShowIcon(); |
| 998 // See comments in IconCapacity(). | 836 // See comments in IconCapacity(). |
| 999 const int extra_padding = | 837 const int extra_padding = |
| 1000 (controller_->ShouldHideCloseButtonForInactiveTabs() || | 838 (controller_->ShouldHideCloseButtonForInactiveTabs() || |
| 1001 (IconCapacity() < 3)) ? 0 : kExtraLeftPaddingToBalanceCloseButtonPadding; | 839 (IconCapacity() < 3)) ? 0 : kExtraLeftPaddingToBalanceCloseButtonPadding; |
| 1002 const int start = lb.x() + extra_padding; | 840 const int start = lb.x() + extra_padding; |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1067 } | 905 } |
| 1068 // The Label will automatically center the font's cap height within the | 906 // The Label will automatically center the font's cap height within the |
| 1069 // provided vertical space. | 907 // provided vertical space. |
| 1070 title_->SetBoundsRect( | 908 title_->SetBoundsRect( |
| 1071 gfx::Rect(title_left, lb.y(), std::max(title_width, 0), lb.height())); | 909 gfx::Rect(title_left, lb.y(), std::max(title_width, 0), lb.height())); |
| 1072 } | 910 } |
| 1073 title_->SetVisible(show_title); | 911 title_->SetVisible(show_title); |
| 1074 } | 912 } |
| 1075 | 913 |
| 1076 void Tab::OnThemeChanged() { | 914 void Tab::OnThemeChanged() { |
| 1077 LoadTabImages(); | |
| 1078 OnButtonColorMaybeChanged(); | 915 OnButtonColorMaybeChanged(); |
| 1079 favicon_ = gfx::ImageSkia(); | 916 favicon_ = gfx::ImageSkia(); |
| 1080 } | 917 } |
| 1081 | 918 |
| 1082 const char* Tab::GetClassName() const { | 919 const char* Tab::GetClassName() const { |
| 1083 return kViewClassName; | 920 return kViewClassName; |
| 1084 } | 921 } |
| 1085 | 922 |
| 1086 bool Tab::GetTooltipText(const gfx::Point& p, base::string16* tooltip) const { | 923 bool Tab::GetTooltipText(const gfx::Point& p, base::string16* tooltip) const { |
| 1087 // Note: Anything that affects the tooltip text should be accounted for when | 924 // Note: Anything that affects the tooltip text should be accounted for when |
| (...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1295 PaintTabBackgroundUsingFillId(canvas, canvas, true, kActiveTabFillId, | 1132 PaintTabBackgroundUsingFillId(canvas, canvas, true, kActiveTabFillId, |
| 1296 has_custom_image, y_offset); | 1133 has_custom_image, y_offset); |
| 1297 canvas->Restore(); | 1134 canvas->Restore(); |
| 1298 } | 1135 } |
| 1299 } | 1136 } |
| 1300 | 1137 |
| 1301 if (showing_icon_) | 1138 if (showing_icon_) |
| 1302 PaintIcon(canvas); | 1139 PaintIcon(canvas); |
| 1303 } | 1140 } |
| 1304 | 1141 |
| 1305 void Tab::PaintImmersiveTab(gfx::Canvas* canvas) { | |
| 1306 // Use transparency for the draw-attention animation. | |
| 1307 int alpha = 255; | |
| 1308 if (pulse_animation_->is_animating() && !data().pinned) { | |
| 1309 alpha = pulse_animation_->CurrentValueBetween( | |
| 1310 255, gfx::ToRoundedInt(255 * kImmersiveTabMinThrobOpacity)); | |
| 1311 } | |
| 1312 | |
| 1313 // Draw a gray rectangle to represent the tab. This works for pinned tabs as | |
| 1314 // well as regular ones. The active tab has a brigher bar. | |
| 1315 SkColor color = | |
| 1316 IsActive() ? kImmersiveActiveTabColor : kImmersiveInactiveTabColor; | |
| 1317 gfx::Rect bar_rect = GetImmersiveBarRect(); | |
| 1318 canvas->FillRect(bar_rect, SkColorSetA(color, alpha)); | |
| 1319 | |
| 1320 // Paint network activity indicator. | |
| 1321 // TODO(jamescook): Replace this placeholder animation with a real one. | |
| 1322 // For now, let's go with a Cylon eye effect, but in blue. | |
| 1323 if (data().network_state != TabRendererData::NETWORK_STATE_NONE) { | |
| 1324 const SkColor kEyeColor = SkColorSetARGB(alpha, 71, 138, 217); | |
| 1325 int eye_width = bar_rect.width() / 3; | |
| 1326 int eye_offset = bar_rect.width() * immersive_loading_step_ / | |
| 1327 kImmersiveLoadingStepCount; | |
| 1328 if (eye_offset + eye_width < bar_rect.width()) { | |
| 1329 // Draw a single indicator strip because it fits inside |bar_rect|. | |
| 1330 gfx::Rect eye_rect( | |
| 1331 bar_rect.x() + eye_offset, 0, eye_width, kImmersiveBarHeight); | |
| 1332 canvas->FillRect(eye_rect, kEyeColor); | |
| 1333 } else { | |
| 1334 // Draw two indicators to simulate the eye "wrapping around" to the left | |
| 1335 // side. The first part fills the remainder of the bar. | |
| 1336 int right_eye_width = bar_rect.width() - eye_offset; | |
| 1337 gfx::Rect right_eye_rect( | |
| 1338 bar_rect.x() + eye_offset, 0, right_eye_width, kImmersiveBarHeight); | |
| 1339 canvas->FillRect(right_eye_rect, kEyeColor); | |
| 1340 // The second part parts the remaining |eye_width| on the left. | |
| 1341 int left_eye_width = eye_offset + eye_width - bar_rect.width(); | |
| 1342 gfx::Rect left_eye_rect( | |
| 1343 bar_rect.x(), 0, left_eye_width, kImmersiveBarHeight); | |
| 1344 canvas->FillRect(left_eye_rect, kEyeColor); | |
| 1345 } | |
| 1346 } | |
| 1347 } | |
| 1348 | |
| 1349 void Tab::PaintInactiveTabBackground(gfx::Canvas* canvas, | 1142 void Tab::PaintInactiveTabBackground(gfx::Canvas* canvas, |
| 1350 const gfx::Path& clip) { | 1143 const gfx::Path& clip) { |
| 1351 bool has_custom_image; | 1144 bool has_custom_image; |
| 1352 int fill_id = controller_->GetBackgroundResourceId(&has_custom_image); | 1145 int fill_id = controller_->GetBackgroundResourceId(&has_custom_image); |
| 1353 | 1146 |
| 1354 // If the theme is providing a custom background image, then its top edge | 1147 // If the theme is providing a custom background image, then its top edge |
| 1355 // should be at the top of the tab. Otherwise, we assume that the background | 1148 // should be at the top of the tab. Otherwise, we assume that the background |
| 1356 // image is a composited foreground + frame image. Note that if the theme is | 1149 // image is a composited foreground + frame image. Note that if the theme is |
| 1357 // only providing a custom frame image, |has_custom_image| will be true, but | 1150 // only providing a custom frame image, |has_custom_image| will be true, but |
| 1358 // we should use the |background_offset_| here. | 1151 // we should use the |background_offset_| here. |
| 1359 const ui::ThemeProvider* tp = GetThemeProvider(); | 1152 const ui::ThemeProvider* tp = GetThemeProvider(); |
| 1360 const int y_offset = tp->HasCustomImage(fill_id) ? 0 : background_offset_.y(); | 1153 const int y_offset = tp->HasCustomImage(fill_id) ? 0 : background_offset_.y(); |
| 1361 | 1154 |
| 1362 // We only cache the image when it's the default image and we're not hovered, | 1155 // We only cache the image when it's the default image and we're not hovered, |
| 1363 // to avoid caching a background image that isn't the same for all tabs. | 1156 // to avoid caching a background image that isn't the same for all tabs. |
| 1364 if (has_custom_image || hover_controller_.ShouldDraw()) { | 1157 if (has_custom_image || hover_controller_.ShouldDraw()) { |
| 1365 PaintTabBackgroundUsingFillId(canvas, canvas, false, fill_id, | 1158 PaintTabBackgroundUsingFillId(canvas, canvas, false, fill_id, |
| 1366 has_custom_image, y_offset); | 1159 has_custom_image, y_offset); |
| 1367 return; | 1160 return; |
| 1368 } | 1161 } |
| 1369 | 1162 |
| 1370 // For efficiency, we don't use separate fill and stroke images unless we | 1163 // For efficiency, we don't use separate fill and stroke images unless we |
| 1371 // really need to clip the stroke and not the fill (for stacked tabs). This | 1164 // really need to clip the stroke and not the fill (for stacked tabs). This |
| 1372 // saves memory and avoids an extra image draw at the cost of recalculating | 1165 // saves memory and avoids an extra image draw at the cost of recalculating |
| 1373 // the images when MaySetClip() toggles. | 1166 // the images when MaySetClip() toggles. |
| 1374 const bool use_fill_and_stroke_images = | 1167 const bool use_fill_and_stroke_images = controller_->MaySetClip(); |
| 1375 controller_->MaySetClip() && | |
| 1376 ui::MaterialDesignController::IsModeMaterial(); | |
| 1377 | 1168 |
| 1378 const ImageCacheEntryMetadata metadata( | 1169 const ImageCacheEntryMetadata metadata( |
| 1379 fill_id, tp->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB), | 1170 tp->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB), |
| 1380 controller_->GetToolbarTopSeparatorColor(), use_fill_and_stroke_images, | 1171 controller_->GetToolbarTopSeparatorColor(), use_fill_and_stroke_images, |
| 1381 canvas->image_scale(), size()); | 1172 canvas->image_scale(), size()); |
| 1382 auto it = std::find_if( | 1173 auto it = std::find_if( |
| 1383 g_image_cache->begin(), g_image_cache->end(), | 1174 g_image_cache->begin(), g_image_cache->end(), |
| 1384 [&metadata](const ImageCacheEntry& e) { return e.metadata == metadata; }); | 1175 [&metadata](const ImageCacheEntry& e) { return e.metadata == metadata; }); |
| 1385 if (it == g_image_cache->end()) { | 1176 if (it == g_image_cache->end()) { |
| 1386 gfx::Canvas tmp_canvas(size(), canvas->image_scale(), false); | 1177 gfx::Canvas tmp_canvas(size(), canvas->image_scale(), false); |
| 1387 if (use_fill_and_stroke_images) { | 1178 if (use_fill_and_stroke_images) { |
| 1388 gfx::Canvas tmp_fill_canvas(size(), canvas->image_scale(), false); | 1179 gfx::Canvas tmp_fill_canvas(size(), canvas->image_scale(), false); |
| 1389 PaintTabBackgroundUsingFillId(&tmp_fill_canvas, &tmp_canvas, false, | 1180 PaintTabBackgroundUsingFillId(&tmp_fill_canvas, &tmp_canvas, false, |
| (...skipping 21 matching lines...) Expand all Loading... |
| 1411 } | 1202 } |
| 1412 canvas->DrawImageInt(it->stroke_image, 0, 0); | 1203 canvas->DrawImageInt(it->stroke_image, 0, 0); |
| 1413 } | 1204 } |
| 1414 | 1205 |
| 1415 void Tab::PaintTabBackgroundUsingFillId(gfx::Canvas* fill_canvas, | 1206 void Tab::PaintTabBackgroundUsingFillId(gfx::Canvas* fill_canvas, |
| 1416 gfx::Canvas* stroke_canvas, | 1207 gfx::Canvas* stroke_canvas, |
| 1417 bool is_active, | 1208 bool is_active, |
| 1418 int fill_id, | 1209 int fill_id, |
| 1419 bool has_custom_image, | 1210 bool has_custom_image, |
| 1420 int y_offset) { | 1211 int y_offset) { |
| 1421 const ui::ThemeProvider* tp = GetThemeProvider(); | 1212 gfx::Path fill; |
| 1422 const SkColor toolbar_color = tp->GetColor(ThemeProperties::COLOR_TOOLBAR); | 1213 SkPaint paint; |
| 1423 gfx::ImageSkia* fill_image = tp->GetImageSkiaNamed(fill_id); | 1214 paint.setAntiAlias(true); |
| 1424 // The tab image needs to be lined up with the background image | |
| 1425 // so that it feels partially transparent. These offsets represent the tab | |
| 1426 // position within the frame background image. | |
| 1427 const int x_offset = GetMirroredX() + background_offset_.x(); | |
| 1428 | 1215 |
| 1429 const SkScalar kMinHoverRadius = 16; | 1216 // Draw the fill. |
| 1430 const SkScalar radius = | 1217 { |
| 1431 std::max(SkFloatToScalar(width() / 4.f), kMinHoverRadius); | 1218 gfx::ScopedCanvas scoped_canvas(fill_canvas); |
| 1432 const bool draw_hover = !is_active && hover_controller_.ShouldDraw(); | 1219 const float scale = fill_canvas->UndoDeviceScaleFactor(); |
| 1433 SkPoint hover_location(gfx::PointToSkPoint(hover_controller_.location())); | 1220 const ui::ThemeProvider* tp = GetThemeProvider(); |
| 1434 const SkColor hover_color = | 1221 const SkColor toolbar_color = tp->GetColor(ThemeProperties::COLOR_TOOLBAR); |
| 1435 SkColorSetA(toolbar_color, hover_controller_.GetAlpha()); | |
| 1436 | 1222 |
| 1437 if (ui::MaterialDesignController::IsModeMaterial()) { | 1223 fill = GetFillPath(scale, size()); |
| 1438 gfx::Path fill; | 1224 { |
| 1439 SkPaint paint; | 1225 gfx::ScopedCanvas clip_scoper(fill_canvas); |
| 1440 paint.setAntiAlias(true); | 1226 fill_canvas->ClipPath(fill, true); |
| 1227 if (has_custom_image) { |
| 1228 gfx::ScopedCanvas scale_scoper(fill_canvas); |
| 1229 fill_canvas->sk_canvas()->scale(scale, scale); |
| 1230 fill_canvas->TileImageInt(*tp->GetImageSkiaNamed(fill_id), |
| 1231 GetMirroredX() + background_offset_.x(), |
| 1232 y_offset, 0, 0, width(), height()); |
| 1233 } else { |
| 1234 paint.setColor( |
| 1235 is_active ? toolbar_color |
| 1236 : tp->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB)); |
| 1237 fill_canvas->DrawRect( |
| 1238 gfx::ScaleToEnclosingRect(GetLocalBounds(), scale), paint); |
| 1239 } |
| 1441 | 1240 |
| 1442 // Draw the fill. | 1241 if (!is_active && hover_controller_.ShouldDraw()) { |
| 1443 { | 1242 SkPoint hover_location( |
| 1444 gfx::ScopedCanvas scoped_canvas(fill_canvas); | 1243 gfx::PointToSkPoint(hover_controller_.location())); |
| 1445 const float scale = fill_canvas->UndoDeviceScaleFactor(); | 1244 hover_location.scale(SkFloatToScalar(scale)); |
| 1446 | 1245 const SkScalar kMinHoverRadius = 16; |
| 1447 fill = GetFillPath(scale, size()); | 1246 const SkScalar radius = |
| 1448 { | 1247 std::max(SkFloatToScalar(width() / 4.f), kMinHoverRadius); |
| 1449 gfx::ScopedCanvas clip_scoper(fill_canvas); | 1248 DrawHighlight(fill_canvas, hover_location, radius * scale, |
| 1450 fill_canvas->ClipPath(fill, true); | 1249 SkColorSetA(toolbar_color, hover_controller_.GetAlpha())); |
| 1451 if (has_custom_image) { | |
| 1452 gfx::ScopedCanvas scale_scoper(fill_canvas); | |
| 1453 fill_canvas->sk_canvas()->scale(scale, scale); | |
| 1454 fill_canvas->TileImageInt(*fill_image, x_offset, y_offset, 0, 0, | |
| 1455 width(), height()); | |
| 1456 } else { | |
| 1457 paint.setColor( | |
| 1458 is_active ? toolbar_color | |
| 1459 : tp->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB)); | |
| 1460 fill_canvas->DrawRect( | |
| 1461 gfx::ScaleToEnclosingRect(GetLocalBounds(), scale), paint); | |
| 1462 } | |
| 1463 if (draw_hover) { | |
| 1464 hover_location.scale(SkFloatToScalar(scale)); | |
| 1465 DrawHighlight(fill_canvas, hover_location, radius * scale, | |
| 1466 hover_color); | |
| 1467 } | |
| 1468 } | 1250 } |
| 1469 } | 1251 } |
| 1252 } |
| 1470 | 1253 |
| 1471 // Draw the stroke. | 1254 // Draw the stroke. |
| 1472 { | 1255 { |
| 1473 gfx::ScopedCanvas scoped_canvas(stroke_canvas); | 1256 gfx::ScopedCanvas scoped_canvas(stroke_canvas); |
| 1474 const float scale = stroke_canvas->UndoDeviceScaleFactor(); | 1257 const float scale = stroke_canvas->UndoDeviceScaleFactor(); |
| 1475 | 1258 |
| 1476 gfx::Path stroke = GetBorderPath(scale, false, false, size()); | 1259 gfx::Path stroke = GetBorderPath(scale, false, false, size()); |
| 1477 Op(stroke, fill, kDifference_SkPathOp, &stroke); | 1260 Op(stroke, fill, kDifference_SkPathOp, &stroke); |
| 1478 if (!is_active) { | 1261 if (!is_active) { |
| 1479 // Clip out the bottom line; this will be drawn for us by | 1262 // Clip out the bottom line; this will be drawn for us by |
| 1480 // TabStrip::PaintChildren(). | 1263 // TabStrip::PaintChildren(). |
| 1481 stroke_canvas->ClipRect( | 1264 stroke_canvas->ClipRect( |
| 1482 gfx::RectF(width() * scale, height() * scale - 1)); | 1265 gfx::RectF(width() * scale, height() * scale - 1)); |
| 1483 } | |
| 1484 paint.setColor(controller_->GetToolbarTopSeparatorColor()); | |
| 1485 stroke_canvas->DrawPath(stroke, paint); | |
| 1486 } | 1266 } |
| 1487 } else { | 1267 paint.setColor(controller_->GetToolbarTopSeparatorColor()); |
| 1488 gfx::Canvas* canvas = stroke_canvas; | 1268 stroke_canvas->DrawPath(stroke, paint); |
| 1489 gfx::Rect rect(gfx::Point(x_offset, y_offset), size()); | |
| 1490 if (draw_hover) { | |
| 1491 // Draw everything to a temporary canvas so we can extract an image for | |
| 1492 // use in masking the hover glow. | |
| 1493 gfx::Canvas background_canvas(size(), canvas->image_scale(), false); | |
| 1494 PaintTabFill(&background_canvas, *fill_image, rect, is_active); | |
| 1495 gfx::ImageSkia background_image(background_canvas.ExtractImageRep()); | |
| 1496 canvas->DrawImageInt(background_image, 0, 0); | |
| 1497 | |
| 1498 gfx::Canvas hover_canvas(size(), canvas->image_scale(), false); | |
| 1499 DrawHighlight(&hover_canvas, hover_location, radius, hover_color); | |
| 1500 gfx::ImageSkia result = gfx::ImageSkiaOperations::CreateMaskedImage( | |
| 1501 gfx::ImageSkia(hover_canvas.ExtractImageRep()), background_image); | |
| 1502 canvas->DrawImageInt(result, 0, 0); | |
| 1503 } else { | |
| 1504 PaintTabFill(canvas, *fill_image, rect, is_active); | |
| 1505 } | |
| 1506 | |
| 1507 // Now draw the stroke, highlights, and shadows around the tab edge. | |
| 1508 TabImages* stroke_images = | |
| 1509 is_active ? &g_active_images : &g_inactive_images; | |
| 1510 canvas->DrawImageInt(*stroke_images->image_l, 0, 0); | |
| 1511 canvas->TileImageInt( | |
| 1512 *stroke_images->image_c, stroke_images->l_width, 0, | |
| 1513 width() - stroke_images->l_width - stroke_images->r_width, height()); | |
| 1514 canvas->DrawImageInt(*stroke_images->image_r, | |
| 1515 width() - stroke_images->r_width, 0); | |
| 1516 } | 1269 } |
| 1517 } | 1270 } |
| 1518 | 1271 |
| 1519 void Tab::PaintPinnedTabTitleChangedIndicatorAndIcon( | 1272 void Tab::PaintPinnedTabTitleChangedIndicatorAndIcon( |
| 1520 gfx::Canvas* canvas, | 1273 gfx::Canvas* canvas, |
| 1521 const gfx::Rect& favicon_draw_bounds) { | 1274 const gfx::Rect& favicon_draw_bounds) { |
| 1522 // The pinned tab title changed indicator consists of two parts: | 1275 // The pinned tab title changed indicator consists of two parts: |
| 1523 // . a clear (totally transparent) part over the bottom right (or left in rtl) | 1276 // . a clear (totally transparent) part over the bottom right (or left in rtl) |
| 1524 // of the favicon. This is done by drawing the favicon to a canvas, then | 1277 // of the favicon. This is done by drawing the favicon to a canvas, then |
| 1525 // drawing the clear part on top of the favicon. | 1278 // drawing the clear part on top of the favicon. |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1591 !should_display_crashed_favicon_) { | 1344 !should_display_crashed_favicon_) { |
| 1592 PaintPinnedTabTitleChangedIndicatorAndIcon(canvas, bounds); | 1345 PaintPinnedTabTitleChangedIndicatorAndIcon(canvas, bounds); |
| 1593 } else if (!favicon_.isNull()) { | 1346 } else if (!favicon_.isNull()) { |
| 1594 canvas->DrawImageInt(favicon_, 0, 0, bounds.width(), bounds.height(), | 1347 canvas->DrawImageInt(favicon_, 0, 0, bounds.width(), bounds.height(), |
| 1595 bounds.x(), bounds.y(), bounds.width(), | 1348 bounds.x(), bounds.y(), bounds.width(), |
| 1596 bounds.height(), false); | 1349 bounds.height(), false); |
| 1597 } | 1350 } |
| 1598 } | 1351 } |
| 1599 | 1352 |
| 1600 void Tab::AdvanceLoadingAnimation() { | 1353 void Tab::AdvanceLoadingAnimation() { |
| 1601 const TabRendererData::NetworkState state = data().network_state; | 1354 if (data().network_state == TabRendererData::NETWORK_STATE_NONE) { |
| 1602 if (controller_->IsImmersiveStyle()) { | |
| 1603 throbber_->SetVisible(false); | |
| 1604 if (state == TabRendererData::NETWORK_STATE_WAITING) { | |
| 1605 // Waiting steps backwards. | |
| 1606 immersive_loading_step_ = | |
| 1607 (immersive_loading_step_ - 1 + kImmersiveLoadingStepCount) % | |
| 1608 kImmersiveLoadingStepCount; | |
| 1609 } else if (state == TabRendererData::NETWORK_STATE_LOADING) { | |
| 1610 immersive_loading_step_ = | |
| 1611 (immersive_loading_step_ + 1) % kImmersiveLoadingStepCount; | |
| 1612 } else { | |
| 1613 immersive_loading_step_ = 0; | |
| 1614 } | |
| 1615 | |
| 1616 SchedulePaintInRect(GetImmersiveBarRect()); | |
| 1617 return; | |
| 1618 } | |
| 1619 | |
| 1620 if (state == TabRendererData::NETWORK_STATE_NONE) { | |
| 1621 throbber_->ResetStartTimes(); | 1355 throbber_->ResetStartTimes(); |
| 1622 throbber_->SetVisible(false); | 1356 throbber_->SetVisible(false); |
| 1623 ScheduleIconPaint(); | 1357 ScheduleIconPaint(); |
| 1624 return; | 1358 return; |
| 1625 } | 1359 } |
| 1626 | 1360 |
| 1627 // Since the throbber can animate for a long time, paint to a separate layer | 1361 // Since the throbber can animate for a long time, paint to a separate layer |
| 1628 // when possible to reduce repaint overhead. | 1362 // when possible to reduce repaint overhead. |
| 1629 const bool paint_to_layer = controller_->CanPaintThrobberToLayer(); | 1363 const bool paint_to_layer = controller_->CanPaintThrobberToLayer(); |
| 1630 if (paint_to_layer != !!throbber_->layer()) { | 1364 if (paint_to_layer != !!throbber_->layer()) { |
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1744 gfx::Rect bounds = favicon_bounds_; | 1478 gfx::Rect bounds = favicon_bounds_; |
| 1745 if (bounds.IsEmpty()) | 1479 if (bounds.IsEmpty()) |
| 1746 return; | 1480 return; |
| 1747 | 1481 |
| 1748 // Extends the area to the bottom when the crash animation is in progress. | 1482 // Extends the area to the bottom when the crash animation is in progress. |
| 1749 if (crash_icon_animation_->is_animating()) | 1483 if (crash_icon_animation_->is_animating()) |
| 1750 bounds.set_height(height() - bounds.y()); | 1484 bounds.set_height(height() - bounds.y()); |
| 1751 bounds.set_x(GetMirroredXForRect(bounds)); | 1485 bounds.set_x(GetMirroredXForRect(bounds)); |
| 1752 SchedulePaintInRect(bounds); | 1486 SchedulePaintInRect(bounds); |
| 1753 } | 1487 } |
| 1754 | |
| 1755 gfx::Rect Tab::GetImmersiveBarRect() const { | |
| 1756 // The main bar is as wide as the normal tab's horizontal top line. | |
| 1757 // This top line of the tab extends a few pixels left and right of the | |
| 1758 // center image due to pixels in the rounded corner images. | |
| 1759 const int kBarPadding = 1; | |
| 1760 int main_bar_left = g_active_images.l_width - kBarPadding; | |
| 1761 int main_bar_right = width() - g_active_images.r_width + kBarPadding; | |
| 1762 return gfx::Rect( | |
| 1763 main_bar_left, 0, main_bar_right - main_bar_left, kImmersiveBarHeight); | |
| 1764 } | |
| OLD | NEW |