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