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 "cc/paint/paint_recorder.h" | |
| 18 #include "chrome/app/vector_icons/vector_icons.h" | 19 #include "chrome/app/vector_icons/vector_icons.h" |
| 19 #include "chrome/browser/themes/theme_properties.h" | 20 #include "chrome/browser/themes/theme_properties.h" |
| 20 #include "chrome/browser/ui/browser.h" | 21 #include "chrome/browser/ui/browser.h" |
| 21 #include "chrome/browser/ui/layout_constants.h" | 22 #include "chrome/browser/ui/layout_constants.h" |
| 22 #include "chrome/browser/ui/tab_contents/core_tab_helper.h" | 23 #include "chrome/browser/ui/tab_contents/core_tab_helper.h" |
| 23 #include "chrome/browser/ui/tabs/tab_utils.h" | 24 #include "chrome/browser/ui/tabs/tab_utils.h" |
| 24 #include "chrome/browser/ui/view_ids.h" | 25 #include "chrome/browser/ui/view_ids.h" |
| 25 #include "chrome/browser/ui/views/frame/browser_view.h" | 26 #include "chrome/browser/ui/views/frame/browser_view.h" |
| 26 #include "chrome/browser/ui/views/tabs/alert_indicator_button.h" | 27 #include "chrome/browser/ui/views/tabs/alert_indicator_button.h" |
| 27 #include "chrome/browser/ui/views/tabs/browser_tab_strip_controller.h" | 28 #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 | 87 |
| 87 // How opaque to make the hover state (out of 1). | 88 // How opaque to make the hover state (out of 1). |
| 88 const double kHoverOpacity = 0.33; | 89 const double kHoverOpacity = 0.33; |
| 89 | 90 |
| 90 // Opacity of the active tab background painted over inactive selected tabs. | 91 // Opacity of the active tab background painted over inactive selected tabs. |
| 91 const double kSelectedTabOpacity = 0.3; | 92 const double kSelectedTabOpacity = 0.3; |
| 92 | 93 |
| 93 // Inactive selected tabs have their throb value scaled by this. | 94 // Inactive selected tabs have their throb value scaled by this. |
| 94 const double kSelectedTabThrobScale = 0.95 - kSelectedTabOpacity; | 95 const double kSelectedTabThrobScale = 0.95 - kSelectedTabOpacity; |
| 95 | 96 |
| 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"; | 97 const char kTabCloseButtonName[] = "TabCloseButton"; |
| 102 | 98 |
| 103 //////////////////////////////////////////////////////////////////////////////// | 99 //////////////////////////////////////////////////////////////////////////////// |
| 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 | 100 // Drawing and utility functions |
| 186 | 101 |
| 187 // Returns the width of the tab endcap at scale 1. More precisely, this is the | 102 // 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; | 103 // 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), | 104 // 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 | 105 // the total width of the endcap from tab outer edge to the inside end of the |
| 191 // stroke inner edge is (GetUnscaledEndcapWidth() * scale) + 1. | 106 // stroke inner edge is (GetUnscaledEndcapWidth() * scale) + 1. |
| 192 float GetUnscaledEndcapWidth() { | 107 float GetUnscaledEndcapWidth() { |
| 193 return GetLayoutInsets(TAB).left() - 0.5f; | 108 return GetLayoutInsets(TAB).left() - 0.5f; |
| 194 } | 109 } |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 212 bool ShouldThemifyFaviconForUrl(const GURL& url) { | 127 bool ShouldThemifyFaviconForUrl(const GURL& url) { |
| 213 return url.SchemeIs(content::kChromeUIScheme) && | 128 return url.SchemeIs(content::kChromeUIScheme) && |
| 214 url.host() != chrome::kChromeUIHelpHost && | 129 url.host() != chrome::kChromeUIHelpHost && |
| 215 url.host() != chrome::kChromeUIUberHost && | 130 url.host() != chrome::kChromeUIUberHost && |
| 216 url.host() != chrome::kChromeUIAppLauncherPageHost; | 131 url.host() != chrome::kChromeUIAppLauncherPageHost; |
| 217 } | 132 } |
| 218 | 133 |
| 219 // Returns a path corresponding to the tab's content region inside the outer | 134 // Returns a path corresponding to the tab's content region inside the outer |
| 220 // stroke. | 135 // stroke. |
| 221 gfx::Path GetFillPath(float scale, const gfx::Size& size) { | 136 gfx::Path GetFillPath(float scale, const gfx::Size& size) { |
| 137 struct FillPathCache { | |
| 138 // The parameters used to build the path. | |
| 139 float scale; | |
| 140 gfx::Size size; | |
| 141 | |
| 142 // The constructed path based on the parameters. | |
| 143 gfx::Path path; | |
| 144 }; | |
| 145 // A cache sorted from most recently used to least. | |
| 146 static auto& cache = *new std::list<FillPathCache>; | |
| 147 static const int kCacheSize = 8; | |
| 148 | |
| 149 // Find a match in the cache and move it to the front and return it. | |
| 150 for (auto it = cache.begin(); it != cache.end(); ++it) { | |
| 151 if (it->scale == scale && it->size == size) { | |
| 152 std::list<FillPathCache> hit; | |
| 153 hit.splice(hit.begin(), cache, it, std::next(it)); | |
| 154 cache.splice(cache.begin(), std::move(hit)); | |
| 155 return cache.front().path; | |
| 156 } | |
| 157 } | |
| 158 | |
| 222 const float right = size.width() * scale; | 159 const float right = size.width() * scale; |
| 223 // The bottom of the tab needs to be pixel-aligned or else when we call | 160 // 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. | 161 // ClipPath with anti-aliasing enabled it can cause artifacts. |
| 225 const float bottom = std::ceil(size.height() * scale); | 162 const float bottom = std::ceil(size.height() * scale); |
| 226 const float unscaled_endcap_width = GetUnscaledEndcapWidth(); | 163 const float unscaled_endcap_width = GetUnscaledEndcapWidth(); |
| 227 | 164 |
| 228 gfx::Path fill; | 165 gfx::Path fill; |
| 229 fill.moveTo(right - 1, bottom); | 166 fill.moveTo(right - 1, bottom); |
| 230 fill.rCubicTo(-0.75 * scale, 0, -1.625 * scale, -0.5 * scale, -2 * scale, | 167 fill.rCubicTo(-0.75 * scale, 0, -1.625 * scale, -0.5 * scale, -2 * scale, |
| 231 -1.5 * scale); | 168 -1.5 * scale); |
| 232 fill.lineTo(right - 1 - (unscaled_endcap_width - 2) * scale, 2.5 * scale); | 169 fill.lineTo(right - 1 - (unscaled_endcap_width - 2) * scale, 2.5 * scale); |
| 233 // Prevent overdraw in the center near minimum width (only happens if | 170 // Prevent overdraw in the center near minimum width (only happens if |
| 234 // scale < 2). We could instead avoid this by increasing the tab inset | 171 // 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 | 172 // 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 | 173 // then overlapped the content on the endcaps, by which point we'd have a |
| 237 // huge mess. | 174 // huge mess. |
| 238 const float scaled_endcap_width = 1 + unscaled_endcap_width * scale; | 175 const float scaled_endcap_width = 1 + unscaled_endcap_width * scale; |
| 239 const float overlap = scaled_endcap_width * 2 - right; | 176 const float overlap = scaled_endcap_width * 2 - right; |
| 240 const float offset = (overlap > 0) ? (overlap / 2) : 0; | 177 const float offset = (overlap > 0) ? (overlap / 2) : 0; |
| 241 fill.rCubicTo(-0.375 * scale, -1 * scale, -1.25 * scale + offset, | 178 fill.rCubicTo(-0.375 * scale, -1 * scale, -1.25 * scale + offset, |
| 242 -1.5 * scale, -2 * scale + offset, -1.5 * scale); | 179 -1.5 * scale, -2 * scale + offset, -1.5 * scale); |
| 243 if (overlap < 0) | 180 if (overlap < 0) |
| 244 fill.lineTo(scaled_endcap_width, scale); | 181 fill.lineTo(scaled_endcap_width, scale); |
| 245 fill.rCubicTo(-0.75 * scale, 0, -1.625 * scale - offset, 0.5 * scale, | 182 fill.rCubicTo(-0.75 * scale, 0, -1.625 * scale - offset, 0.5 * scale, |
| 246 -2 * scale - offset, 1.5 * scale); | 183 -2 * scale - offset, 1.5 * scale); |
| 247 fill.lineTo(1 + 2 * scale, bottom - 1.5 * scale); | 184 fill.lineTo(1 + 2 * scale, bottom - 1.5 * scale); |
| 248 fill.rCubicTo(-0.375 * scale, scale, -1.25 * scale, 1.5 * scale, -2 * scale, | 185 fill.rCubicTo(-0.375 * scale, scale, -1.25 * scale, 1.5 * scale, -2 * scale, |
| 249 1.5 * scale); | 186 1.5 * scale); |
| 250 fill.close(); | 187 fill.close(); |
| 188 | |
| 189 // If the cache is max size we need to drop the least recently used from the | |
| 190 // cache. Then add the new path to the front of the cache. | |
| 191 if (cache.size() >= kCacheSize) | |
| 192 cache.pop_back(); | |
| 193 cache.emplace_front(); | |
| 194 cache.front().scale = scale; | |
| 195 cache.front().size = size; | |
| 196 cache.front().path = fill; | |
| 197 | |
| 251 return fill; | 198 return fill; |
| 252 } | 199 } |
| 253 | 200 |
| 254 // Returns a path corresponding to the tab's outer border for a given tab |size| | 201 // 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 | 202 // 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, | 203 // 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 | 204 // 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. | 205 // uses this for Fitts' Law purposes in maximized/fullscreen mode. |
| 259 gfx::Path GetBorderPath(float scale, | 206 gfx::Path GetBorderPath(float scale, |
| 260 bool unscale_at_end, | 207 bool unscale_at_end, |
| (...skipping 266 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 527 alert_indicator_button_(nullptr), | 474 alert_indicator_button_(nullptr), |
| 528 close_button_(nullptr), | 475 close_button_(nullptr), |
| 529 title_(new views::Label()), | 476 title_(new views::Label()), |
| 530 tab_activated_with_last_tap_down_(false), | 477 tab_activated_with_last_tap_down_(false), |
| 531 hover_controller_(this), | 478 hover_controller_(this), |
| 532 showing_icon_(false), | 479 showing_icon_(false), |
| 533 showing_alert_indicator_(false), | 480 showing_alert_indicator_(false), |
| 534 showing_close_button_(false), | 481 showing_close_button_(false), |
| 535 button_color_(SK_ColorTRANSPARENT) { | 482 button_color_(SK_ColorTRANSPARENT) { |
| 536 DCHECK(controller); | 483 DCHECK(controller); |
| 537 InitTabResources(); | |
| 538 | 484 |
| 539 // So we get don't get enter/exit on children and don't prematurely stop the | 485 // So we get don't get enter/exit on children and don't prematurely stop the |
| 540 // hover. | 486 // hover. |
| 541 set_notify_enter_exit_on_child(true); | 487 set_notify_enter_exit_on_child(true); |
| 542 | 488 |
| 543 set_id(VIEW_ID_TAB); | 489 set_id(VIEW_ID_TAB); |
| 544 | 490 |
| 545 SetBorder(views::CreateEmptyBorder(GetLayoutInsets(TAB))); | 491 SetBorder(views::CreateEmptyBorder(GetLayoutInsets(TAB))); |
| 546 | 492 |
| 547 title_->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD); | 493 title_->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD); |
| (...skipping 568 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1116 else | 1062 else |
| 1117 StopPulse(); | 1063 StopPulse(); |
| 1118 } | 1064 } |
| 1119 | 1065 |
| 1120 void Tab::PaintTab(gfx::Canvas* canvas, const gfx::Path& clip) { | 1066 void Tab::PaintTab(gfx::Canvas* canvas, const gfx::Path& clip) { |
| 1121 const int kActiveTabFillId = | 1067 const int kActiveTabFillId = |
| 1122 GetThemeProvider()->HasCustomImage(IDR_THEME_TOOLBAR) ? IDR_THEME_TOOLBAR | 1068 GetThemeProvider()->HasCustomImage(IDR_THEME_TOOLBAR) ? IDR_THEME_TOOLBAR |
| 1123 : 0; | 1069 : 0; |
| 1124 const int y_offset = -GetLayoutInsets(TAB).top(); | 1070 const int y_offset = -GetLayoutInsets(TAB).top(); |
| 1125 if (IsActive()) { | 1071 if (IsActive()) { |
| 1126 PaintTabBackgroundUsingFillId(canvas, canvas, true, kActiveTabFillId, | 1072 PaintTabBackground(canvas, true /* active */, kActiveTabFillId, y_offset, |
| 1127 y_offset); | 1073 nullptr /* clip */); |
| 1128 } else { | 1074 } else { |
| 1129 PaintInactiveTabBackground(canvas, clip); | 1075 PaintInactiveTabBackground(canvas, clip); |
| 1130 | 1076 |
| 1131 const double throb_value = GetThrobValue(); | 1077 const double throb_value = GetThrobValue(); |
| 1132 if (throb_value > 0) { | 1078 if (throb_value > 0) { |
| 1133 canvas->SaveLayerAlpha(gfx::ToRoundedInt(throb_value * 0xff), | 1079 canvas->SaveLayerAlpha(gfx::ToRoundedInt(throb_value * 0xff), |
| 1134 GetLocalBounds()); | 1080 GetLocalBounds()); |
| 1135 PaintTabBackgroundUsingFillId(canvas, canvas, true, kActiveTabFillId, | 1081 PaintTabBackground(canvas, true /* active */, kActiveTabFillId, y_offset, |
| 1136 y_offset); | 1082 nullptr /* clip */); |
| 1137 canvas->Restore(); | 1083 canvas->Restore(); |
| 1138 } | 1084 } |
| 1139 } | 1085 } |
| 1140 | 1086 |
| 1141 if (showing_icon_) | 1087 if (showing_icon_) |
| 1142 PaintIcon(canvas); | 1088 PaintIcon(canvas); |
| 1143 } | 1089 } |
| 1144 | 1090 |
| 1145 void Tab::PaintInactiveTabBackground(gfx::Canvas* canvas, | 1091 void Tab::PaintInactiveTabBackground(gfx::Canvas* canvas, |
| 1146 const gfx::Path& clip) { | 1092 const gfx::Path& clip) { |
| 1147 bool has_custom_image; | 1093 bool has_custom_image; |
| 1148 int fill_id = controller_->GetBackgroundResourceId(&has_custom_image); | 1094 int fill_id = controller_->GetBackgroundResourceId(&has_custom_image); |
| 1149 const ui::ThemeProvider* tp = GetThemeProvider(); | |
| 1150 | 1095 |
| 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) { | 1096 if (has_custom_image) { |
| 1097 const ui::ThemeProvider* tp = GetThemeProvider(); | |
| 1098 | |
| 1154 // If the theme is providing a custom background image, then its top edge | 1099 // 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 | 1100 // 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 | 1101 // 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, | 1102 // is only providing a custom frame image, |has_custom_image| will be true, |
| 1158 // but we should use the |background_offset_| here. | 1103 // but we should use the |background_offset_| here. |
| 1159 const int y_offset = | 1104 int y_offset = tp->HasCustomImage(fill_id) ? 0 : background_offset_.y(); |
| 1160 tp->HasCustomImage(fill_id) ? 0 : background_offset_.y(); | 1105 PaintTabBackground(canvas, false /* active */, fill_id, y_offset, |
| 1161 PaintTabBackgroundUsingFillId(canvas, canvas, false, fill_id, y_offset); | 1106 nullptr /* clip */); |
| 1162 return; | 1107 } else { |
| 1108 PaintTabBackground(canvas, false /* active */, 0 /* fill_id */, | |
| 1109 0 /* y_offset */, | |
| 1110 controller_->MaySetClip() ? &clip : nullptr); | |
| 1163 } | 1111 } |
| 1164 if (hover_controller_.ShouldDraw()) { | 1112 } |
| 1165 PaintTabBackgroundUsingFillId(canvas, canvas, false, 0, 0); | 1113 |
| 1114 void Tab::PaintTabBackground(gfx::Canvas* canvas, | |
| 1115 bool active, | |
| 1116 int fill_id, | |
| 1117 int y_offset, | |
| 1118 const gfx::Path* clip) { | |
| 1119 // |y_offset| is only set when |fill_id| is being used. | |
| 1120 DCHECK(!y_offset || !fill_id); | |
|
Peter Kasting
2017/04/07 00:59:19
Nit: I'm not sure why this DCHECK is here since, w
danakj
2017/04/07 19:10:49
I added this because I think it should be an invar
danakj
2017/04/07 21:31:08
Oh I had the logic inverted though. I miss DCHECK_
| |
| 1121 | |
| 1122 const ui::ThemeProvider* tp = GetThemeProvider(); | |
| 1123 SkColor active_color = tp->GetColor(ThemeProperties::COLOR_TOOLBAR); | |
|
Peter Kasting
2017/04/07 00:59:19
Nit: These could all be const
danakj
2017/04/07 19:10:49
Sure, I guess I don't see much reason for putting
| |
| 1124 SkColor inactive_color = tp->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB); | |
| 1125 bool hover = !active && hover_controller_.ShouldDraw(); | |
| 1126 | |
| 1127 // If there is a |fill_id| we don't try to cache. This could be improved | |
| 1128 // but would require knowing then the image from the ThemeProvider had been | |
| 1129 // changed, and invalidating when more position values changed. | |
|
Peter Kasting
2017/04/07 00:59:19
Nit: more position values changed -> the X coordin
danakj
2017/04/07 19:10:49
Also background_offset_ (both x() which is used di
| |
| 1130 // Stroke does not depend on |fill_id|. | |
| 1131 // Similarly, if |hover| effects should be shown, we don't try to cache since | |
| 1132 // they change on every invalidation and we would need to invalidate the cache | |
| 1133 // based on the hover states. | |
| 1134 if (fill_id || hover) { | |
| 1135 gfx::Path fill_path = GetFillPath(canvas->image_scale(), size()); | |
| 1136 PaintTabBackgroundFill(canvas, fill_path, active, hover, active_color, | |
| 1137 inactive_color, fill_id, y_offset); | |
| 1138 gfx::ScopedCanvas scoped_canvas(clip ? canvas : nullptr); | |
| 1139 if (clip) | |
| 1140 canvas->sk_canvas()->clipPath(*clip, SkClipOp::kDifference, true); | |
|
Peter Kasting
2017/04/07 00:59:19
These bits about scoping a canvas, then clipping,
danakj
2017/04/07 19:10:49
It's a nice thought, the problem is we have to cha
Peter Kasting
2017/04/08 00:59:37
The way I can think of, which may not be a win, is
| |
| 1141 PaintTabBackgroundStroke(canvas, fill_path, active); | |
| 1166 return; | 1142 return; |
| 1167 } | 1143 } |
| 1168 | 1144 |
| 1169 // For efficiency, we don't use separate fill and stroke images unless we | 1145 BackgroundCache& cache = |
| 1170 // really need to clip the stroke and not the fill (for stacked tabs). This | 1146 active ? background_active_cache_ : background_inactive_cache_; |
| 1171 // saves memory and avoids an extra image draw at the cost of recalculating | 1147 bool matches_cache = cache.scale == canvas->image_scale() && |
|
Peter Kasting
2017/04/07 00:59:19
Nit: I liked how the old code had a metadata struc
danakj
2017/04/07 19:10:49
It feels like it should be less, but It's a more l
Peter Kasting
2017/04/07 23:34:03
It's definitely more verbose overall. I'm OK with
danakj
2017/04/18 16:02:42
The duplicate storage I mean like..
float scale
| |
| 1172 // the images when MaySetClip() toggles. | 1148 cache.size == size() && |
| 1173 const bool use_fill_and_stroke_images = controller_->MaySetClip(); | 1149 cache.active_color == active_color && |
| 1150 cache.inactive_color == inactive_color; | |
| 1151 if (!matches_cache) { | |
| 1152 gfx::Path fill_path = GetFillPath(canvas->image_scale(), size()); | |
| 1153 cc::PaintRecorder recorder; | |
| 1174 | 1154 |
| 1175 const ImageCacheEntryMetadata metadata( | 1155 { |
| 1176 tp->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB), | 1156 gfx::Canvas cache_canvas( |
| 1177 controller_->GetToolbarTopSeparatorColor(), use_fill_and_stroke_images, | 1157 recorder.beginRecording(size().width(), size().height()), |
| 1178 canvas->image_scale(), size()); | 1158 canvas->image_scale()); |
| 1179 auto it = std::find_if( | 1159 PaintTabBackgroundFill(&cache_canvas, fill_path, active, hover, |
| 1180 g_image_cache->begin(), g_image_cache->end(), | 1160 active_color, inactive_color, fill_id, y_offset); |
| 1181 [&metadata](const ImageCacheEntry& e) { return e.metadata == metadata; }); | 1161 cache.fill_record = recorder.finishRecordingAsPicture(); |
| 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 } | 1162 } |
| 1196 if (g_image_cache->size() > kMaxImageCacheSize) | 1163 { |
| 1197 g_image_cache->pop_back(); | 1164 gfx::Canvas cache_canvas( |
| 1198 it = g_image_cache->begin(); | 1165 recorder.beginRecording(size().width(), size().height()), |
| 1166 canvas->image_scale()); | |
| 1167 PaintTabBackgroundStroke(canvas, fill_path, active); | |
| 1168 cache.stroke_record = recorder.finishRecordingAsPicture(); | |
| 1169 } | |
| 1170 | |
| 1171 cache.scale = canvas->image_scale(); | |
| 1172 cache.size = size(); | |
| 1173 cache.active_color = active_color; | |
| 1174 cache.inactive_color = inactive_color; | |
| 1199 } | 1175 } |
| 1200 | 1176 |
| 1201 gfx::ScopedCanvas scoped_canvas( | 1177 canvas->sk_canvas()->PlaybackPaintRecord(cache.fill_record); |
| 1202 use_fill_and_stroke_images ? canvas : nullptr); | 1178 gfx::ScopedCanvas scoped_canvas(clip ? canvas : nullptr); |
| 1203 if (use_fill_and_stroke_images) { | 1179 if (clip) |
| 1204 canvas->DrawImageInt(it->fill_image, 0, 0); | 1180 canvas->sk_canvas()->clipPath(*clip, SkClipOp::kDifference, true); |
| 1205 canvas->sk_canvas()->clipPath(clip, SkClipOp::kDifference, true); | 1181 canvas->sk_canvas()->PlaybackPaintRecord(cache.stroke_record); |
| 1206 } | |
| 1207 canvas->DrawImageInt(it->stroke_image, 0, 0); | |
| 1208 } | 1182 } |
| 1209 | 1183 |
| 1210 void Tab::PaintTabBackgroundUsingFillId(gfx::Canvas* fill_canvas, | 1184 void Tab::PaintTabBackgroundFill(gfx::Canvas* canvas, |
| 1211 gfx::Canvas* stroke_canvas, | 1185 const gfx::Path& fill_path, |
| 1212 bool is_active, | 1186 bool active, |
| 1213 int fill_id, | 1187 bool hover, |
| 1214 int y_offset) { | 1188 SkColor active_color, |
| 1215 gfx::Path fill; | 1189 SkColor inactive_color, |
| 1216 cc::PaintFlags flags; | 1190 int fill_id, |
| 1217 flags.setAntiAlias(true); | 1191 int y_offset) { |
| 1192 gfx::ScopedCanvas scoped_canvas(canvas); | |
| 1193 const float scale = canvas->UndoDeviceScaleFactor(); | |
| 1218 | 1194 |
| 1219 // Draw the fill. | 1195 canvas->ClipPath(fill_path, true); |
| 1220 { | 1196 if (fill_id) { |
| 1221 gfx::ScopedCanvas scoped_canvas(fill_canvas); | |
| 1222 const float scale = fill_canvas->UndoDeviceScaleFactor(); | |
| 1223 const ui::ThemeProvider* tp = GetThemeProvider(); | 1197 const ui::ThemeProvider* tp = GetThemeProvider(); |
|
Peter Kasting
2017/04/07 00:59:19
Nit: Just inline this below?
danakj
2017/04/07 19:10:49
Done.
| |
| 1224 const SkColor toolbar_color = tp->GetColor(ThemeProperties::COLOR_TOOLBAR); | |
| 1225 | 1198 |
| 1226 fill = GetFillPath(scale, size()); | 1199 gfx::ScopedCanvas scale_scoper(canvas); |
| 1227 { | 1200 canvas->sk_canvas()->scale(scale, scale); |
| 1228 gfx::ScopedCanvas clip_scoper(fill_canvas); | 1201 canvas->TileImageInt(*tp->GetImageSkiaNamed(fill_id), |
| 1229 fill_canvas->ClipPath(fill, true); | 1202 GetMirroredX() + background_offset_.x(), y_offset, 0, |
| 1230 if (fill_id) { | 1203 0, width(), height()); |
| 1231 gfx::ScopedCanvas scale_scoper(fill_canvas); | 1204 } else { |
| 1232 fill_canvas->sk_canvas()->scale(scale, scale); | 1205 cc::PaintFlags flags; |
| 1233 fill_canvas->TileImageInt(*tp->GetImageSkiaNamed(fill_id), | 1206 flags.setAntiAlias(true); |
| 1234 GetMirroredX() + background_offset_.x(), | 1207 flags.setColor(active ? active_color : inactive_color); |
| 1235 y_offset, 0, 0, width(), height()); | 1208 canvas->DrawRect(gfx::ScaleToEnclosingRect(GetLocalBounds(), scale), flags); |
| 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 } | 1209 } |
| 1256 | 1210 |
| 1257 // Draw the stroke. | 1211 if (hover) { |
|
Peter Kasting
2017/04/07 00:59:20
Nit: Maybe name this |draw_highlight| or |draw_hov
danakj
2017/04/07 19:10:49
went with paint_hover_effect. "draw" is a overload
| |
| 1258 { | 1212 SkPoint hover_location(gfx::PointToSkPoint(hover_controller_.location())); |
| 1259 gfx::ScopedCanvas scoped_canvas(stroke_canvas); | 1213 hover_location.scale(SkFloatToScalar(scale)); |
| 1260 const float scale = stroke_canvas->UndoDeviceScaleFactor(); | 1214 const SkScalar kMinHoverRadius = 16; |
| 1261 | 1215 const SkScalar radius = |
| 1262 gfx::Path stroke = GetBorderPath(scale, false, false, size()); | 1216 std::max(SkFloatToScalar(width() / 4.f), kMinHoverRadius); |
| 1263 Op(stroke, fill, kDifference_SkPathOp, &stroke); | 1217 DrawHighlight(canvas, hover_location, radius * scale, |
| 1264 if (!is_active) { | 1218 SkColorSetA(active_color, hover_controller_.GetAlpha())); |
| 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 } | 1219 } |
| 1273 } | 1220 } |
| 1274 | 1221 |
| 1222 void Tab::PaintTabBackgroundStroke(gfx::Canvas* canvas, | |
| 1223 const gfx::Path& fill_path, | |
| 1224 bool active) { | |
| 1225 SkColor color = controller_->GetToolbarTopSeparatorColor(); | |
| 1226 | |
| 1227 gfx::ScopedCanvas scoped_canvas(canvas); | |
| 1228 const float scale = canvas->UndoDeviceScaleFactor(); | |
| 1229 | |
| 1230 gfx::Path stroke = GetBorderPath(scale, false, false, size()); | |
| 1231 Op(stroke, fill_path, kDifference_SkPathOp, &stroke); | |
| 1232 if (!active) { | |
| 1233 // Clip out the bottom line; this will be drawn for us by | |
| 1234 // TabStrip::PaintChildren(). | |
| 1235 canvas->ClipRect(gfx::RectF(width() * scale, height() * scale - 1)); | |
| 1236 } | |
| 1237 cc::PaintFlags flags; | |
| 1238 flags.setAntiAlias(true); | |
| 1239 flags.setColor(color); | |
| 1240 canvas->DrawPath(stroke, flags); | |
| 1241 } | |
| 1242 | |
| 1275 void Tab::PaintPinnedTabTitleChangedIndicatorAndIcon( | 1243 void Tab::PaintPinnedTabTitleChangedIndicatorAndIcon( |
| 1276 gfx::Canvas* canvas, | 1244 gfx::Canvas* canvas, |
| 1277 const gfx::Rect& favicon_draw_bounds) { | 1245 const gfx::Rect& favicon_draw_bounds) { |
| 1278 // The pinned tab title changed indicator consists of two parts: | 1246 // The pinned tab title changed indicator consists of two parts: |
| 1279 // . a clear (totally transparent) part over the bottom right (or left in rtl) | 1247 // . 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 | 1248 // of the favicon. This is done by drawing the favicon to a canvas, then |
| 1281 // drawing the clear part on top of the favicon. | 1249 // drawing the clear part on top of the favicon. |
| 1282 // . a circle in the bottom right (or left in rtl) of the favicon. | 1250 // . a circle in the bottom right (or left in rtl) of the favicon. |
| 1283 if (!favicon_.isNull()) { | 1251 if (!favicon_.isNull()) { |
| 1284 const float kIndicatorCropRadius = 4.5; | 1252 const float kIndicatorCropRadius = 4.5; |
| (...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1486 gfx::Rect bounds = favicon_bounds_; | 1454 gfx::Rect bounds = favicon_bounds_; |
| 1487 if (bounds.IsEmpty()) | 1455 if (bounds.IsEmpty()) |
| 1488 return; | 1456 return; |
| 1489 | 1457 |
| 1490 // Extends the area to the bottom when the crash animation is in progress. | 1458 // Extends the area to the bottom when the crash animation is in progress. |
| 1491 if (crash_icon_animation_->is_animating()) | 1459 if (crash_icon_animation_->is_animating()) |
| 1492 bounds.set_height(height() - bounds.y()); | 1460 bounds.set_height(height() - bounds.y()); |
| 1493 bounds.set_x(GetMirroredXForRect(bounds)); | 1461 bounds.set_x(GetMirroredXForRect(bounds)); |
| 1494 SchedulePaintInRect(bounds); | 1462 SchedulePaintInRect(bounds); |
| 1495 } | 1463 } |
| 1464 | |
| 1465 Tab::BackgroundCache::BackgroundCache() = default; | |
| 1466 Tab::BackgroundCache::~BackgroundCache() = default; | |
| OLD | NEW |