Chromium Code Reviews| 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 |
| 11 #include "base/command_line.h" | 11 #include "base/command_line.h" |
| 12 #include "base/debug/alias.h" | 12 #include "base/debug/alias.h" |
| 13 #include "base/macros.h" | 13 #include "base/macros.h" |
| 14 #include "base/metrics/user_metrics.h" | 14 #include "base/metrics/user_metrics.h" |
| 15 #include "base/strings/utf_string_conversions.h" | 15 #include "base/strings/utf_string_conversions.h" |
| 16 #include "build/build_config.h" | 16 #include "build/build_config.h" |
| 17 #include "cc/paint/paint_shader.h" | 17 #include "cc/paint/paint_flags.h" |
| 18 #include "chrome/app/vector_icons/vector_icons.h" | 18 #include "chrome/app/vector_icons/vector_icons.h" |
| 19 #include "chrome/browser/themes/theme_properties.h" | 19 #include "chrome/browser/themes/theme_properties.h" |
| 20 #include "chrome/browser/ui/browser.h" | 20 #include "chrome/browser/ui/browser.h" |
| 21 #include "chrome/browser/ui/layout_constants.h" | 21 #include "chrome/browser/ui/layout_constants.h" |
| 22 #include "chrome/browser/ui/tab_contents/core_tab_helper.h" | 22 #include "chrome/browser/ui/tab_contents/core_tab_helper.h" |
| 23 #include "chrome/browser/ui/tabs/tab_utils.h" | 23 #include "chrome/browser/ui/tabs/tab_utils.h" |
| 24 #include "chrome/browser/ui/view_ids.h" | 24 #include "chrome/browser/ui/view_ids.h" |
| 25 #include "chrome/browser/ui/views/frame/browser_view.h" | 25 #include "chrome/browser/ui/views/frame/browser_view.h" |
| 26 #include "chrome/browser/ui/views/tabs/alert_indicator_button.h" | 26 #include "chrome/browser/ui/views/tabs/alert_indicator_button.h" |
| 27 #include "chrome/browser/ui/views/tabs/browser_tab_strip_controller.h" | 27 #include "chrome/browser/ui/views/tabs/browser_tab_strip_controller.h" |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 86 | 86 |
| 87 // How opaque to make the hover state (out of 1). | 87 // How opaque to make the hover state (out of 1). |
| 88 const double kHoverOpacity = 0.33; | 88 const double kHoverOpacity = 0.33; |
| 89 | 89 |
| 90 // Opacity of the active tab background painted over inactive selected tabs. | 90 // Opacity of the active tab background painted over inactive selected tabs. |
| 91 const double kSelectedTabOpacity = 0.3; | 91 const double kSelectedTabOpacity = 0.3; |
| 92 | 92 |
| 93 // Inactive selected tabs have their throb value scaled by this. | 93 // Inactive selected tabs have their throb value scaled by this. |
| 94 const double kSelectedTabThrobScale = 0.95 - kSelectedTabOpacity; | 94 const double kSelectedTabThrobScale = 0.95 - kSelectedTabOpacity; |
| 95 | 95 |
| 96 // Max number of images to cache. This has to be at least two since rounding | |
| 97 // errors may lead to tabs in the same tabstrip having different sizes. | |
| 98 // 8 = normal/incognito, active/inactive, 2 sizes within tabstrip. | |
| 99 const size_t kMaxImageCacheSize = 8; | |
| 100 | |
| 101 const char kTabCloseButtonName[] = "TabCloseButton"; | 96 const char kTabCloseButtonName[] = "TabCloseButton"; |
| 102 | 97 |
| 103 //////////////////////////////////////////////////////////////////////////////// | 98 //////////////////////////////////////////////////////////////////////////////// |
| 104 // ImageCacheEntryMetadata | |
| 105 // | |
| 106 // All metadata necessary to uniquely identify a cached image. | |
| 107 struct ImageCacheEntryMetadata { | |
| 108 ImageCacheEntryMetadata(SkColor fill_color, | |
| 109 SkColor stroke_color, | |
| 110 bool use_fill_and_stroke_images, | |
| 111 float scale_factor, | |
| 112 const gfx::Size& size); | |
| 113 | |
| 114 ~ImageCacheEntryMetadata(); | |
| 115 | |
| 116 bool operator==(const ImageCacheEntryMetadata& rhs) const; | |
| 117 | |
| 118 SkColor fill_color; | |
| 119 SkColor stroke_color; | |
| 120 bool use_fill_and_stroke_images; | |
| 121 float scale_factor; | |
| 122 gfx::Size size; | |
| 123 }; | |
| 124 | |
| 125 ImageCacheEntryMetadata::ImageCacheEntryMetadata( | |
| 126 SkColor fill_color, | |
| 127 SkColor stroke_color, | |
| 128 bool use_fill_and_stroke_images, | |
| 129 float scale_factor, | |
| 130 const gfx::Size& size) | |
| 131 : fill_color(fill_color), | |
| 132 stroke_color(stroke_color), | |
| 133 use_fill_and_stroke_images(use_fill_and_stroke_images), | |
| 134 scale_factor(scale_factor), | |
| 135 size(size) {} | |
| 136 | |
| 137 ImageCacheEntryMetadata::~ImageCacheEntryMetadata() {} | |
| 138 | |
| 139 bool ImageCacheEntryMetadata::operator==( | |
| 140 const ImageCacheEntryMetadata& rhs) const { | |
| 141 return fill_color == rhs.fill_color && stroke_color == rhs.stroke_color && | |
| 142 use_fill_and_stroke_images == rhs.use_fill_and_stroke_images && | |
| 143 scale_factor == rhs.scale_factor && size == rhs.size; | |
| 144 } | |
| 145 | |
| 146 //////////////////////////////////////////////////////////////////////////////// | |
| 147 // ImageCacheEntry and cache management | |
| 148 // | |
| 149 // A cached image and the metadata used to generate it. | |
| 150 struct ImageCacheEntry { | |
| 151 ImageCacheEntry(const ImageCacheEntryMetadata& metadata, | |
| 152 const gfx::ImageSkia& fill_image, | |
| 153 const gfx::ImageSkia& stroke_image); | |
| 154 ~ImageCacheEntry(); | |
| 155 | |
| 156 ImageCacheEntryMetadata metadata; | |
| 157 gfx::ImageSkia fill_image; | |
| 158 gfx::ImageSkia stroke_image; | |
| 159 }; | |
| 160 | |
| 161 ImageCacheEntry::ImageCacheEntry(const ImageCacheEntryMetadata& metadata, | |
| 162 const gfx::ImageSkia& fill_image, | |
| 163 const gfx::ImageSkia& stroke_image) | |
| 164 : metadata(metadata), fill_image(fill_image), stroke_image(stroke_image) {} | |
| 165 | |
| 166 ImageCacheEntry::~ImageCacheEntry() {} | |
| 167 | |
| 168 typedef std::list<ImageCacheEntry> ImageCache; | |
| 169 | |
| 170 // As the majority of the tabs are inactive, and painting tabs is slowish, | |
| 171 // we cache a handful of the inactive tab backgrounds here. | |
| 172 static ImageCache* g_image_cache = nullptr; | |
| 173 | |
| 174 // Performs a one-time initialization of static resources such as tab images. | |
| 175 void InitTabResources() { | |
| 176 static bool initialized = false; | |
| 177 if (initialized) | |
| 178 return; | |
| 179 | |
| 180 initialized = true; | |
| 181 g_image_cache = new ImageCache(); | |
| 182 } | |
| 183 | |
| 184 //////////////////////////////////////////////////////////////////////////////// | |
| 185 // Drawing and utility functions | 99 // Drawing and utility functions |
| 186 | 100 |
| 187 // Returns the width of the tab endcap at scale 1. More precisely, this is the | 101 // Returns the width of the tab endcap at scale 1. More precisely, this is the |
| 188 // width of the curve making up either the outer or inner edge of the stroke; | 102 // width of the curve making up either the outer or inner edge of the stroke; |
| 189 // since these two curves are horizontally offset by 1 px (regardless of scale), | 103 // since these two curves are horizontally offset by 1 px (regardless of scale), |
| 190 // the total width of the endcap from tab outer edge to the inside end of the | 104 // the total width of the endcap from tab outer edge to the inside end of the |
| 191 // stroke inner edge is (GetUnscaledEndcapWidth() * scale) + 1. | 105 // stroke inner edge is (GetUnscaledEndcapWidth() * scale) + 1. |
| 192 float GetUnscaledEndcapWidth() { | 106 float GetUnscaledEndcapWidth() { |
| 193 return GetLayoutInsets(TAB).left() - 0.5f; | 107 return GetLayoutInsets(TAB).left() - 0.5f; |
| 194 } | 108 } |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 212 bool ShouldThemifyFaviconForUrl(const GURL& url) { | 126 bool ShouldThemifyFaviconForUrl(const GURL& url) { |
| 213 return url.SchemeIs(content::kChromeUIScheme) && | 127 return url.SchemeIs(content::kChromeUIScheme) && |
| 214 url.host() != chrome::kChromeUIHelpHost && | 128 url.host() != chrome::kChromeUIHelpHost && |
| 215 url.host() != chrome::kChromeUIUberHost && | 129 url.host() != chrome::kChromeUIUberHost && |
| 216 url.host() != chrome::kChromeUIAppLauncherPageHost; | 130 url.host() != chrome::kChromeUIAppLauncherPageHost; |
| 217 } | 131 } |
| 218 | 132 |
| 219 // Returns a path corresponding to the tab's content region inside the outer | 133 // Returns a path corresponding to the tab's content region inside the outer |
| 220 // stroke. | 134 // stroke. |
| 221 gfx::Path GetFillPath(float scale, const gfx::Size& size) { | 135 gfx::Path GetFillPath(float scale, const gfx::Size& size) { |
| 136 struct FillPathCache { | |
| 137 // The parameters used to build the path. | |
| 138 float scale; | |
| 139 gfx::Size size; | |
| 140 | |
| 141 // The constructed path based on the parameters. | |
| 142 gfx::Path path; | |
| 143 }; | |
| 144 // A cache sorted from most recently used to least. | |
| 145 static auto& cache = *new std::list<FillPathCache>; | |
|
Peter Kasting
2017/04/06 18:49:45
Can we use a base::MRUCache instead of manually ro
danakj
2017/04/07 19:10:49
We could, I had looked there first, but it just lo
Peter Kasting
2017/04/07 23:34:02
Would it make sense to move MRUCache to one of the
| |
| 146 static const int kCacheSize = 8; | |
|
Peter Kasting
2017/04/06 18:49:45
Nit: constexpr size_t
danakj
2017/04/07 19:10:49
Done.
| |
| 147 | |
| 148 // Find a match in the cache and move it to the front and return it. | |
| 149 for (auto it = cache.begin(); it != cache.end(); ++it) { | |
| 150 if (it->scale == scale && it->size == size) { | |
| 151 std::list<FillPathCache> hit; | |
| 152 hit.splice(hit.begin(), cache, it, std::next(it)); | |
| 153 cache.splice(cache.begin(), std::move(hit)); | |
|
Peter Kasting
2017/04/07 23:34:02
Can we just use std::rotate? This should work wit
danakj
2017/04/18 16:02:42
I deleted the cache here anyhow, but I think rotat
| |
| 154 return cache.front().path; | |
| 155 } | |
| 156 } | |
| 157 | |
| 222 const float right = size.width() * scale; | 158 const float right = size.width() * scale; |
| 223 // The bottom of the tab needs to be pixel-aligned or else when we call | 159 // The bottom of the tab needs to be pixel-aligned or else when we call |
| 224 // ClipPath with anti-aliasing enabled it can cause artifacts. | 160 // ClipPath with anti-aliasing enabled it can cause artifacts. |
| 225 const float bottom = std::ceil(size.height() * scale); | 161 const float bottom = std::ceil(size.height() * scale); |
| 226 const float unscaled_endcap_width = GetUnscaledEndcapWidth(); | 162 const float unscaled_endcap_width = GetUnscaledEndcapWidth(); |
| 227 | 163 |
| 228 gfx::Path fill; | 164 gfx::Path fill; |
| 229 fill.moveTo(right - 1, bottom); | 165 fill.moveTo(right - 1, bottom); |
| 230 fill.rCubicTo(-0.75 * scale, 0, -1.625 * scale, -0.5 * scale, -2 * scale, | 166 fill.rCubicTo(-0.75 * scale, 0, -1.625 * scale, -0.5 * scale, -2 * scale, |
| 231 -1.5 * scale); | 167 -1.5 * scale); |
| 232 fill.lineTo(right - 1 - (unscaled_endcap_width - 2) * scale, 2.5 * scale); | 168 fill.lineTo(right - 1 - (unscaled_endcap_width - 2) * scale, 2.5 * scale); |
| 233 // Prevent overdraw in the center near minimum width (only happens if | 169 // Prevent overdraw in the center near minimum width (only happens if |
| 234 // scale < 2). We could instead avoid this by increasing the tab inset | 170 // scale < 2). We could instead avoid this by increasing the tab inset |
| 235 // values, but that would shift all the content inward as well, unless we | 171 // values, but that would shift all the content inward as well, unless we |
| 236 // then overlapped the content on the endcaps, by which point we'd have a | 172 // then overlapped the content on the endcaps, by which point we'd have a |
| 237 // huge mess. | 173 // huge mess. |
| 238 const float scaled_endcap_width = 1 + unscaled_endcap_width * scale; | 174 const float scaled_endcap_width = 1 + unscaled_endcap_width * scale; |
| 239 const float overlap = scaled_endcap_width * 2 - right; | 175 const float overlap = scaled_endcap_width * 2 - right; |
| 240 const float offset = (overlap > 0) ? (overlap / 2) : 0; | 176 const float offset = (overlap > 0) ? (overlap / 2) : 0; |
| 241 fill.rCubicTo(-0.375 * scale, -1 * scale, -1.25 * scale + offset, | 177 fill.rCubicTo(-0.375 * scale, -1 * scale, -1.25 * scale + offset, |
| 242 -1.5 * scale, -2 * scale + offset, -1.5 * scale); | 178 -1.5 * scale, -2 * scale + offset, -1.5 * scale); |
| 243 if (overlap < 0) | 179 if (overlap < 0) |
| 244 fill.lineTo(scaled_endcap_width, scale); | 180 fill.lineTo(scaled_endcap_width, scale); |
| 245 fill.rCubicTo(-0.75 * scale, 0, -1.625 * scale - offset, 0.5 * scale, | 181 fill.rCubicTo(-0.75 * scale, 0, -1.625 * scale - offset, 0.5 * scale, |
| 246 -2 * scale - offset, 1.5 * scale); | 182 -2 * scale - offset, 1.5 * scale); |
| 247 fill.lineTo(1 + 2 * scale, bottom - 1.5 * scale); | 183 fill.lineTo(1 + 2 * scale, bottom - 1.5 * scale); |
| 248 fill.rCubicTo(-0.375 * scale, scale, -1.25 * scale, 1.5 * scale, -2 * scale, | 184 fill.rCubicTo(-0.375 * scale, scale, -1.25 * scale, 1.5 * scale, -2 * scale, |
| 249 1.5 * scale); | 185 1.5 * scale); |
| 250 fill.close(); | 186 fill.close(); |
| 187 | |
| 188 // If the cache is max size we need to drop the least recently used from the | |
| 189 // cache. Then add the new path to the front of the cache. | |
| 190 if (cache.size() >= kCacheSize) | |
| 191 cache.pop_back(); | |
| 192 cache.emplace_front(); | |
| 193 cache.front().scale = scale; | |
|
Peter Kasting
2017/04/06 18:49:45
Nit: I'd rather define a constructor so we can jus
danakj
2017/04/07 19:10:49
Done. Made a constructor and a HasMatchingParamete
| |
| 194 cache.front().size = size; | |
| 195 cache.front().path = fill; | |
| 196 | |
| 251 return fill; | 197 return fill; |
| 252 } | 198 } |
| 253 | 199 |
| 254 // Returns a path corresponding to the tab's outer border for a given tab |size| | 200 // Returns a path corresponding to the tab's outer border for a given tab |size| |
| 255 // and |scale|. If |unscale_at_end| is true, this path will be normalized to a | 201 // and |scale|. If |unscale_at_end| is true, this path will be normalized to a |
| 256 // 1x scale by scaling by 1/scale before returning. If |extend_to_top| is true, | 202 // 1x scale by scaling by 1/scale before returning. If |extend_to_top| is true, |
| 257 // the path is extended vertically to the top of the tab bounds. The caller | 203 // the path is extended vertically to the top of the tab bounds. The caller |
| 258 // uses this for Fitts' Law purposes in maximized/fullscreen mode. | 204 // uses this for Fitts' Law purposes in maximized/fullscreen mode. |
| 259 gfx::Path GetBorderPath(float scale, | 205 gfx::Path GetBorderPath(float scale, |
| 260 bool unscale_at_end, | 206 bool unscale_at_end, |
| (...skipping 266 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 527 alert_indicator_button_(nullptr), | 473 alert_indicator_button_(nullptr), |
| 528 close_button_(nullptr), | 474 close_button_(nullptr), |
| 529 title_(new views::Label()), | 475 title_(new views::Label()), |
| 530 tab_activated_with_last_tap_down_(false), | 476 tab_activated_with_last_tap_down_(false), |
| 531 hover_controller_(this), | 477 hover_controller_(this), |
| 532 showing_icon_(false), | 478 showing_icon_(false), |
| 533 showing_alert_indicator_(false), | 479 showing_alert_indicator_(false), |
| 534 showing_close_button_(false), | 480 showing_close_button_(false), |
| 535 button_color_(SK_ColorTRANSPARENT) { | 481 button_color_(SK_ColorTRANSPARENT) { |
| 536 DCHECK(controller); | 482 DCHECK(controller); |
| 537 InitTabResources(); | |
| 538 | 483 |
| 539 // So we get don't get enter/exit on children and don't prematurely stop the | 484 // So we get don't get enter/exit on children and don't prematurely stop the |
| 540 // hover. | 485 // hover. |
| 541 set_notify_enter_exit_on_child(true); | 486 set_notify_enter_exit_on_child(true); |
| 542 | 487 |
| 543 set_id(VIEW_ID_TAB); | 488 set_id(VIEW_ID_TAB); |
| 544 | 489 |
| 545 SetBorder(views::CreateEmptyBorder(GetLayoutInsets(TAB))); | 490 SetBorder(views::CreateEmptyBorder(GetLayoutInsets(TAB))); |
| 546 | 491 |
| 547 title_->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD); | 492 title_->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD); |
| (...skipping 568 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1116 else | 1061 else |
| 1117 StopPulse(); | 1062 StopPulse(); |
| 1118 } | 1063 } |
| 1119 | 1064 |
| 1120 void Tab::PaintTab(gfx::Canvas* canvas, const gfx::Path& clip) { | 1065 void Tab::PaintTab(gfx::Canvas* canvas, const gfx::Path& clip) { |
| 1121 const int kActiveTabFillId = | 1066 const int kActiveTabFillId = |
| 1122 GetThemeProvider()->HasCustomImage(IDR_THEME_TOOLBAR) ? IDR_THEME_TOOLBAR | 1067 GetThemeProvider()->HasCustomImage(IDR_THEME_TOOLBAR) ? IDR_THEME_TOOLBAR |
| 1123 : 0; | 1068 : 0; |
| 1124 const int y_offset = -GetLayoutInsets(TAB).top(); | 1069 const int y_offset = -GetLayoutInsets(TAB).top(); |
| 1125 if (IsActive()) { | 1070 if (IsActive()) { |
| 1126 PaintTabBackgroundUsingFillId(canvas, canvas, true, kActiveTabFillId, | 1071 PaintTabBackground(canvas, true /* active */, kActiveTabFillId, y_offset, |
| 1127 y_offset); | 1072 nullptr /* clip */); |
| 1128 } else { | 1073 } else { |
| 1129 PaintInactiveTabBackground(canvas, clip); | 1074 PaintInactiveTabBackground(canvas, clip); |
| 1130 | 1075 |
| 1131 const double throb_value = GetThrobValue(); | 1076 const double throb_value = GetThrobValue(); |
| 1132 if (throb_value > 0) { | 1077 if (throb_value > 0) { |
| 1133 canvas->SaveLayerAlpha(gfx::ToRoundedInt(throb_value * 0xff), | 1078 canvas->SaveLayerAlpha(gfx::ToRoundedInt(throb_value * 0xff), |
| 1134 GetLocalBounds()); | 1079 GetLocalBounds()); |
| 1135 PaintTabBackgroundUsingFillId(canvas, canvas, true, kActiveTabFillId, | 1080 PaintTabBackground(canvas, true /* active */, kActiveTabFillId, y_offset, |
| 1136 y_offset); | 1081 nullptr /* clip */); |
| 1137 canvas->Restore(); | 1082 canvas->Restore(); |
| 1138 } | 1083 } |
| 1139 } | 1084 } |
| 1140 | 1085 |
| 1141 if (showing_icon_) | 1086 if (showing_icon_) |
| 1142 PaintIcon(canvas); | 1087 PaintIcon(canvas); |
| 1143 } | 1088 } |
| 1144 | 1089 |
| 1145 void Tab::PaintInactiveTabBackground(gfx::Canvas* canvas, | 1090 void Tab::PaintInactiveTabBackground(gfx::Canvas* canvas, |
| 1146 const gfx::Path& clip) { | 1091 const gfx::Path& clip) { |
| 1147 bool has_custom_image; | 1092 bool has_custom_image; |
| 1148 int fill_id = controller_->GetBackgroundResourceId(&has_custom_image); | 1093 int fill_id = controller_->GetBackgroundResourceId(&has_custom_image); |
| 1149 const ui::ThemeProvider* tp = GetThemeProvider(); | |
| 1150 | 1094 |
| 1151 // We only cache the image when it's the default image and we're not hovered, | |
| 1152 // to avoid caching a background image that isn't the same for all tabs. | |
| 1153 if (has_custom_image) { | 1095 if (has_custom_image) { |
| 1096 const ui::ThemeProvider* tp = GetThemeProvider(); | |
| 1097 | |
| 1154 // If the theme is providing a custom background image, then its top edge | 1098 // If the theme is providing a custom background image, then its top edge |
| 1155 // should be at the top of the tab. Otherwise, we assume that the background | 1099 // should be at the top of the tab. Otherwise, we assume that the background |
| 1156 // image is a composited foreground + frame image. Note that if the theme | 1100 // image is a composited foreground + frame image. Note that if the theme |
| 1157 // is only providing a custom frame image, |has_custom_image| will be true, | 1101 // is only providing a custom frame image, |has_custom_image| will be true, |
| 1158 // but we should use the |background_offset_| here. | 1102 // but we should use the |background_offset_| here. |
| 1159 const int y_offset = | 1103 int y_offset = tp->HasCustomImage(fill_id) ? 0 : background_offset_.y(); |
| 1160 tp->HasCustomImage(fill_id) ? 0 : background_offset_.y(); | 1104 PaintTabBackground(canvas, false /* active */, fill_id, y_offset, |
| 1161 PaintTabBackgroundUsingFillId(canvas, canvas, false, fill_id, y_offset); | 1105 nullptr /* clip */); |
| 1162 return; | 1106 } else { |
| 1163 } | 1107 PaintTabBackground(canvas, false /* active */, 0 /* fill_id */, |
| 1164 if (hover_controller_.ShouldDraw()) { | 1108 0 /* y_offset */, |
| 1165 PaintTabBackgroundUsingFillId(canvas, canvas, false, 0, 0); | 1109 controller_->MaySetClip() ? &clip : nullptr); |
| 1166 return; | |
| 1167 } | |
| 1168 | |
| 1169 // For efficiency, we don't use separate fill and stroke images unless we | |
| 1170 // really need to clip the stroke and not the fill (for stacked tabs). This | |
| 1171 // saves memory and avoids an extra image draw at the cost of recalculating | |
| 1172 // the images when MaySetClip() toggles. | |
| 1173 const bool use_fill_and_stroke_images = controller_->MaySetClip(); | |
| 1174 | |
| 1175 const ImageCacheEntryMetadata metadata( | |
| 1176 tp->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB), | |
| 1177 controller_->GetToolbarTopSeparatorColor(), use_fill_and_stroke_images, | |
| 1178 canvas->image_scale(), size()); | |
| 1179 auto it = std::find_if( | |
| 1180 g_image_cache->begin(), g_image_cache->end(), | |
| 1181 [&metadata](const ImageCacheEntry& e) { return e.metadata == metadata; }); | |
| 1182 if (it == g_image_cache->end()) { | |
| 1183 gfx::Canvas tmp_canvas(size(), canvas->image_scale(), false); | |
| 1184 if (use_fill_and_stroke_images) { | |
| 1185 gfx::Canvas tmp_fill_canvas(size(), canvas->image_scale(), false); | |
| 1186 PaintTabBackgroundUsingFillId(&tmp_fill_canvas, &tmp_canvas, false, 0, 0); | |
| 1187 g_image_cache->emplace_front( | |
| 1188 metadata, gfx::ImageSkia(tmp_fill_canvas.ExtractImageRep()), | |
| 1189 gfx::ImageSkia(tmp_canvas.ExtractImageRep())); | |
| 1190 } else { | |
| 1191 PaintTabBackgroundUsingFillId(&tmp_canvas, &tmp_canvas, false, 0, 0); | |
| 1192 g_image_cache->emplace_front( | |
| 1193 metadata, gfx::ImageSkia(), | |
| 1194 gfx::ImageSkia(tmp_canvas.ExtractImageRep())); | |
| 1195 } | |
| 1196 if (g_image_cache->size() > kMaxImageCacheSize) | |
| 1197 g_image_cache->pop_back(); | |
| 1198 it = g_image_cache->begin(); | |
| 1199 } | |
| 1200 | |
| 1201 gfx::ScopedCanvas scoped_canvas( | |
| 1202 use_fill_and_stroke_images ? canvas : nullptr); | |
| 1203 if (use_fill_and_stroke_images) { | |
| 1204 canvas->DrawImageInt(it->fill_image, 0, 0); | |
| 1205 canvas->sk_canvas()->clipPath(clip, SkClipOp::kDifference, true); | |
| 1206 } | |
| 1207 canvas->DrawImageInt(it->stroke_image, 0, 0); | |
| 1208 } | |
| 1209 | |
| 1210 void Tab::PaintTabBackgroundUsingFillId(gfx::Canvas* fill_canvas, | |
| 1211 gfx::Canvas* stroke_canvas, | |
| 1212 bool is_active, | |
| 1213 int fill_id, | |
| 1214 int y_offset) { | |
| 1215 gfx::Path fill; | |
| 1216 cc::PaintFlags flags; | |
| 1217 flags.setAntiAlias(true); | |
| 1218 | |
| 1219 // Draw the fill. | |
| 1220 { | |
| 1221 gfx::ScopedCanvas scoped_canvas(fill_canvas); | |
| 1222 const float scale = fill_canvas->UndoDeviceScaleFactor(); | |
| 1223 const ui::ThemeProvider* tp = GetThemeProvider(); | |
| 1224 const SkColor toolbar_color = tp->GetColor(ThemeProperties::COLOR_TOOLBAR); | |
| 1225 | |
| 1226 fill = GetFillPath(scale, size()); | |
| 1227 { | |
| 1228 gfx::ScopedCanvas clip_scoper(fill_canvas); | |
| 1229 fill_canvas->ClipPath(fill, true); | |
| 1230 if (fill_id) { | |
| 1231 gfx::ScopedCanvas scale_scoper(fill_canvas); | |
| 1232 fill_canvas->sk_canvas()->scale(scale, scale); | |
| 1233 fill_canvas->TileImageInt(*tp->GetImageSkiaNamed(fill_id), | |
| 1234 GetMirroredX() + background_offset_.x(), | |
| 1235 y_offset, 0, 0, width(), height()); | |
| 1236 } else { | |
| 1237 flags.setColor( | |
| 1238 is_active ? toolbar_color | |
| 1239 : tp->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB)); | |
| 1240 fill_canvas->DrawRect( | |
| 1241 gfx::ScaleToEnclosingRect(GetLocalBounds(), scale), flags); | |
| 1242 } | |
| 1243 | |
| 1244 if (!is_active && hover_controller_.ShouldDraw()) { | |
| 1245 SkPoint hover_location( | |
| 1246 gfx::PointToSkPoint(hover_controller_.location())); | |
| 1247 hover_location.scale(SkFloatToScalar(scale)); | |
| 1248 const SkScalar kMinHoverRadius = 16; | |
| 1249 const SkScalar radius = | |
| 1250 std::max(SkFloatToScalar(width() / 4.f), kMinHoverRadius); | |
| 1251 DrawHighlight(fill_canvas, hover_location, radius * scale, | |
| 1252 SkColorSetA(toolbar_color, hover_controller_.GetAlpha())); | |
| 1253 } | |
| 1254 } | |
| 1255 } | |
| 1256 | |
| 1257 // Draw the stroke. | |
| 1258 { | |
| 1259 gfx::ScopedCanvas scoped_canvas(stroke_canvas); | |
| 1260 const float scale = stroke_canvas->UndoDeviceScaleFactor(); | |
| 1261 | |
| 1262 gfx::Path stroke = GetBorderPath(scale, false, false, size()); | |
| 1263 Op(stroke, fill, kDifference_SkPathOp, &stroke); | |
| 1264 if (!is_active) { | |
| 1265 // Clip out the bottom line; this will be drawn for us by | |
| 1266 // TabStrip::PaintChildren(). | |
| 1267 stroke_canvas->ClipRect( | |
| 1268 gfx::RectF(width() * scale, height() * scale - 1)); | |
| 1269 } | |
| 1270 flags.setColor(controller_->GetToolbarTopSeparatorColor()); | |
| 1271 stroke_canvas->DrawPath(stroke, flags); | |
| 1272 } | 1110 } |
| 1273 } | 1111 } |
| 1274 | 1112 |
| 1113 void Tab::PaintTabBackground(gfx::Canvas* canvas, | |
| 1114 bool active, | |
| 1115 int fill_id, | |
| 1116 int y_offset, | |
| 1117 const gfx::Path* clip) { | |
| 1118 gfx::Path fill_path = GetFillPath(canvas->image_scale(), size()); | |
| 1119 | |
| 1120 PaintTabBackgroundFill(canvas, fill_path, active, fill_id, y_offset); | |
| 1121 { | |
|
Peter Kasting
2017/04/06 18:49:45
Nit: You could save a line or two and reduce the i
danakj
2017/04/07 19:10:49
Done.
| |
| 1122 gfx::ScopedCanvas scoped_canvas(clip ? canvas : nullptr); | |
| 1123 if (clip) | |
| 1124 canvas->sk_canvas()->clipPath(*clip, SkClipOp::kDifference, true); | |
| 1125 PaintTabBackgroundStroke(canvas, fill_path, active); | |
| 1126 } | |
| 1127 } | |
| 1128 | |
| 1129 void Tab::PaintTabBackgroundFill(gfx::Canvas* canvas, | |
| 1130 const gfx::Path& fill_path, | |
| 1131 bool active, | |
| 1132 int fill_id, | |
| 1133 int y_offset) { | |
| 1134 const ui::ThemeProvider* tp = GetThemeProvider(); | |
| 1135 SkColor active_color = tp->GetColor(ThemeProperties::COLOR_TOOLBAR); | |
| 1136 SkColor inactive_color = tp->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB); | |
| 1137 | |
| 1138 gfx::ScopedCanvas scoped_canvas(canvas); | |
| 1139 const float scale = canvas->UndoDeviceScaleFactor(); | |
| 1140 | |
| 1141 canvas->ClipPath(fill_path, true); | |
| 1142 if (fill_id) { | |
| 1143 gfx::ScopedCanvas scale_scoper(canvas); | |
| 1144 canvas->sk_canvas()->scale(scale, scale); | |
| 1145 canvas->TileImageInt(*tp->GetImageSkiaNamed(fill_id), | |
| 1146 GetMirroredX() + background_offset_.x(), y_offset, 0, | |
| 1147 0, width(), height()); | |
| 1148 } else { | |
| 1149 cc::PaintFlags flags; | |
| 1150 flags.setAntiAlias(true); | |
| 1151 flags.setColor(active ? active_color : inactive_color); | |
| 1152 canvas->DrawRect(gfx::ScaleToEnclosingRect(GetLocalBounds(), scale), flags); | |
| 1153 } | |
| 1154 | |
| 1155 if (!active && hover_controller_.ShouldDraw()) { | |
| 1156 SkPoint hover_location(gfx::PointToSkPoint(hover_controller_.location())); | |
| 1157 hover_location.scale(SkFloatToScalar(scale)); | |
| 1158 const SkScalar kMinHoverRadius = 16; | |
| 1159 const SkScalar radius = | |
| 1160 std::max(SkFloatToScalar(width() / 4.f), kMinHoverRadius); | |
| 1161 DrawHighlight(canvas, hover_location, radius * scale, | |
| 1162 SkColorSetA(active_color, hover_controller_.GetAlpha())); | |
| 1163 } | |
| 1164 } | |
| 1165 | |
| 1166 void Tab::PaintTabBackgroundStroke(gfx::Canvas* canvas, | |
| 1167 const gfx::Path& fill_path, | |
| 1168 bool active) { | |
| 1169 SkColor color = controller_->GetToolbarTopSeparatorColor(); | |
| 1170 | |
| 1171 gfx::ScopedCanvas scoped_canvas(canvas); | |
| 1172 const float scale = canvas->UndoDeviceScaleFactor(); | |
| 1173 | |
| 1174 gfx::Path stroke = GetBorderPath(scale, false, false, size()); | |
| 1175 Op(stroke, fill_path, kDifference_SkPathOp, &stroke); | |
| 1176 if (!active) { | |
| 1177 // Clip out the bottom line; this will be drawn for us by | |
| 1178 // TabStrip::PaintChildren(). | |
| 1179 canvas->ClipRect(gfx::RectF(width() * scale, height() * scale - 1)); | |
| 1180 } | |
| 1181 cc::PaintFlags flags; | |
| 1182 flags.setAntiAlias(true); | |
| 1183 flags.setColor(color); | |
| 1184 canvas->DrawPath(stroke, flags); | |
| 1185 } | |
| 1186 | |
| 1275 void Tab::PaintPinnedTabTitleChangedIndicatorAndIcon( | 1187 void Tab::PaintPinnedTabTitleChangedIndicatorAndIcon( |
| 1276 gfx::Canvas* canvas, | 1188 gfx::Canvas* canvas, |
| 1277 const gfx::Rect& favicon_draw_bounds) { | 1189 const gfx::Rect& favicon_draw_bounds) { |
| 1278 // The pinned tab title changed indicator consists of two parts: | 1190 // The pinned tab title changed indicator consists of two parts: |
| 1279 // . a clear (totally transparent) part over the bottom right (or left in rtl) | 1191 // . a clear (totally transparent) part over the bottom right (or left in rtl) |
| 1280 // of the favicon. This is done by drawing the favicon to a canvas, then | 1192 // of the favicon. This is done by drawing the favicon to a canvas, then |
| 1281 // drawing the clear part on top of the favicon. | 1193 // drawing the clear part on top of the favicon. |
| 1282 // . a circle in the bottom right (or left in rtl) of the favicon. | 1194 // . a circle in the bottom right (or left in rtl) of the favicon. |
| 1283 if (!favicon_.isNull()) { | 1195 if (!favicon_.isNull()) { |
| 1284 const float kIndicatorCropRadius = 4.5; | 1196 const float kIndicatorCropRadius = 4.5; |
| (...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1486 gfx::Rect bounds = favicon_bounds_; | 1398 gfx::Rect bounds = favicon_bounds_; |
| 1487 if (bounds.IsEmpty()) | 1399 if (bounds.IsEmpty()) |
| 1488 return; | 1400 return; |
| 1489 | 1401 |
| 1490 // Extends the area to the bottom when the crash animation is in progress. | 1402 // Extends the area to the bottom when the crash animation is in progress. |
| 1491 if (crash_icon_animation_->is_animating()) | 1403 if (crash_icon_animation_->is_animating()) |
| 1492 bounds.set_height(height() - bounds.y()); | 1404 bounds.set_height(height() - bounds.y()); |
| 1493 bounds.set_x(GetMirroredXForRect(bounds)); | 1405 bounds.set_x(GetMirroredXForRect(bounds)); |
| 1494 SchedulePaintInRect(bounds); | 1406 SchedulePaintInRect(bounds); |
| 1495 } | 1407 } |
| OLD | NEW |