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 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
143 }; | 143 }; |
144 | 144 |
145 //////////////////////////////////////////////////////////////////////////////// | 145 //////////////////////////////////////////////////////////////////////////////// |
146 // ImageCacheEntryMetadata | 146 // ImageCacheEntryMetadata |
147 // | 147 // |
148 // All metadata necessary to uniquely identify a cached image. | 148 // All metadata necessary to uniquely identify a cached image. |
149 struct ImageCacheEntryMetadata { | 149 struct ImageCacheEntryMetadata { |
150 ImageCacheEntryMetadata(int resource_id, | 150 ImageCacheEntryMetadata(int resource_id, |
151 SkColor fill_color, | 151 SkColor fill_color, |
152 SkColor stroke_color, | 152 SkColor stroke_color, |
| 153 bool use_fill_and_stroke_images, |
153 ui::ScaleFactor scale_factor, | 154 ui::ScaleFactor scale_factor, |
154 const gfx::Size& size); | 155 const gfx::Size& size); |
155 | 156 |
156 ~ImageCacheEntryMetadata(); | 157 ~ImageCacheEntryMetadata(); |
157 | 158 |
158 bool operator==(const ImageCacheEntryMetadata& rhs) const; | 159 bool operator==(const ImageCacheEntryMetadata& rhs) const; |
159 | 160 |
160 int resource_id; // Only needed by pre-MD | 161 int resource_id; // Only needed by pre-MD |
161 SkColor fill_color; // Both colors only needed by MD | 162 SkColor fill_color; // Both colors only needed by MD |
162 SkColor stroke_color; | 163 SkColor stroke_color; |
| 164 bool use_fill_and_stroke_images; |
163 ui::ScaleFactor scale_factor; | 165 ui::ScaleFactor scale_factor; |
164 gfx::Size size; | 166 gfx::Size size; |
165 }; | 167 }; |
166 | 168 |
167 ImageCacheEntryMetadata::ImageCacheEntryMetadata(int resource_id, | 169 ImageCacheEntryMetadata::ImageCacheEntryMetadata( |
168 SkColor fill_color, | 170 int resource_id, |
169 SkColor stroke_color, | 171 SkColor fill_color, |
170 ui::ScaleFactor scale_factor, | 172 SkColor stroke_color, |
171 const gfx::Size& size) | 173 bool use_fill_and_stroke_images, |
| 174 ui::ScaleFactor scale_factor, |
| 175 const gfx::Size& size) |
172 : resource_id(resource_id), | 176 : resource_id(resource_id), |
173 fill_color(fill_color), | 177 fill_color(fill_color), |
174 stroke_color(stroke_color), | 178 stroke_color(stroke_color), |
| 179 use_fill_and_stroke_images(use_fill_and_stroke_images), |
175 scale_factor(scale_factor), | 180 scale_factor(scale_factor), |
176 size(size) { | 181 size(size) { |
177 DCHECK_NE(ui::SCALE_FACTOR_NONE, scale_factor); | 182 DCHECK_NE(ui::SCALE_FACTOR_NONE, scale_factor); |
178 | 183 |
179 // Some fields are only relevant for pre-MD vs. MD. Erase the irrelevant ones | 184 // Some fields are only relevant for pre-MD vs. MD. Erase the irrelevant ones |
180 // so they don't cause incorrect cache misses. | 185 // so they don't cause incorrect cache misses. |
181 // TODO(pkasting): Remove |resource_id| field when non-MD code is deleted. | 186 // TODO(pkasting): Remove |resource_id| field when non-MD code is deleted. |
182 if (ui::MaterialDesignController::IsModeMaterial()) | 187 if (ui::MaterialDesignController::IsModeMaterial()) |
183 resource_id = 0; | 188 resource_id = 0; |
184 else | 189 else |
185 fill_color = stroke_color = SK_ColorTRANSPARENT; | 190 fill_color = stroke_color = SK_ColorTRANSPARENT; |
186 } | 191 } |
187 | 192 |
188 ImageCacheEntryMetadata::~ImageCacheEntryMetadata() {} | 193 ImageCacheEntryMetadata::~ImageCacheEntryMetadata() {} |
189 | 194 |
190 bool ImageCacheEntryMetadata::operator==( | 195 bool ImageCacheEntryMetadata::operator==( |
191 const ImageCacheEntryMetadata& rhs) const { | 196 const ImageCacheEntryMetadata& rhs) const { |
192 return resource_id == rhs.resource_id && fill_color == rhs.fill_color && | 197 return resource_id == rhs.resource_id && fill_color == rhs.fill_color && |
193 stroke_color == rhs.stroke_color && scale_factor == rhs.scale_factor && | 198 stroke_color == rhs.stroke_color && |
194 size == rhs.size; | 199 use_fill_and_stroke_images == rhs.use_fill_and_stroke_images && |
| 200 scale_factor == rhs.scale_factor && size == rhs.size; |
195 } | 201 } |
196 | 202 |
197 //////////////////////////////////////////////////////////////////////////////// | 203 //////////////////////////////////////////////////////////////////////////////// |
198 // ImageCacheEntry and cache management | 204 // ImageCacheEntry and cache management |
199 // | 205 // |
200 // A cached image and the metadata used to generate it. | 206 // A cached image and the metadata used to generate it. |
201 struct ImageCacheEntry { | 207 struct ImageCacheEntry { |
202 ImageCacheEntry(const ImageCacheEntryMetadata& metadata, | 208 ImageCacheEntry(const ImageCacheEntryMetadata& metadata, |
203 const gfx::ImageSkia& image); | 209 const gfx::ImageSkia& fill_image, |
| 210 const gfx::ImageSkia& stroke_image); |
204 ~ImageCacheEntry(); | 211 ~ImageCacheEntry(); |
205 | 212 |
206 ImageCacheEntryMetadata metadata; | 213 ImageCacheEntryMetadata metadata; |
207 gfx::ImageSkia image; | 214 gfx::ImageSkia fill_image; |
| 215 gfx::ImageSkia stroke_image; |
208 }; | 216 }; |
209 | 217 |
210 ImageCacheEntry::ImageCacheEntry(const ImageCacheEntryMetadata& metadata, | 218 ImageCacheEntry::ImageCacheEntry(const ImageCacheEntryMetadata& metadata, |
211 const gfx::ImageSkia& image) | 219 const gfx::ImageSkia& fill_image, |
212 : metadata(metadata), image(image) {} | 220 const gfx::ImageSkia& stroke_image) |
| 221 : metadata(metadata), fill_image(fill_image), stroke_image(stroke_image) {} |
213 | 222 |
214 ImageCacheEntry::~ImageCacheEntry() {} | 223 ImageCacheEntry::~ImageCacheEntry() {} |
215 | 224 |
216 typedef std::list<ImageCacheEntry> ImageCache; | 225 typedef std::list<ImageCacheEntry> ImageCache; |
217 | 226 |
218 // As the majority of the tabs are inactive, and painting tabs is slowish, | 227 // As the majority of the tabs are inactive, and painting tabs is slowish, |
219 // we cache a handful of the inactive tab backgrounds here. | 228 // we cache a handful of the inactive tab backgrounds here. |
220 static ImageCache* g_image_cache = nullptr; | 229 static ImageCache* g_image_cache = nullptr; |
221 | 230 |
222 struct TabImages { | 231 struct TabImages { |
(...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
465 theme_r.height() - toolbar_overlap, false); | 474 theme_r.height() - toolbar_overlap, false); |
466 | 475 |
467 // Draw center. Instead of masking out the top portion we simply skip over it | 476 // Draw center. Instead of masking out the top portion we simply skip over it |
468 // by incrementing by the top padding, since it's a simple rectangle. | 477 // by incrementing by the top padding, since it's a simple rectangle. |
469 rect.Inset(g_mask_images.l_width, tab_insets.top(), g_mask_images.r_width, | 478 rect.Inset(g_mask_images.l_width, tab_insets.top(), g_mask_images.r_width, |
470 toolbar_overlap); | 479 toolbar_overlap); |
471 canvas->TileImageInt(fill_image, rect.x(), rect.y(), g_mask_images.l_width, | 480 canvas->TileImageInt(fill_image, rect.x(), rect.y(), g_mask_images.l_width, |
472 tab_insets.top(), rect.width(), rect.height()); | 481 tab_insets.top(), rect.width(), rect.height()); |
473 } | 482 } |
474 | 483 |
475 void PaintTabBackgroundUsingParams(gfx::Canvas* canvas, | 484 void PaintTabBackgroundUsingParams(gfx::Canvas* fill_canvas, |
| 485 gfx::Canvas* stroke_canvas, |
476 views::GlowHoverController* hc, | 486 views::GlowHoverController* hc, |
477 const PaintBackgroundParams& params) { | 487 const PaintBackgroundParams& params) { |
478 const gfx::Rect& rect = params.rect; | 488 const gfx::Rect& rect = params.rect; |
479 const SkScalar kMinHoverRadius = 16; | 489 const SkScalar kMinHoverRadius = 16; |
480 const SkScalar radius = | 490 const SkScalar radius = |
481 std::max(SkFloatToScalar(rect.width() / 4.f), kMinHoverRadius); | 491 std::max(SkFloatToScalar(rect.width() / 4.f), kMinHoverRadius); |
482 const bool draw_hover = !params.is_active && hc; | 492 const bool draw_hover = !params.is_active && hc; |
483 SkPoint hover_location( | 493 SkPoint hover_location( |
484 gfx::PointToSkPoint(draw_hover ? hc->location() : gfx::Point())); | 494 gfx::PointToSkPoint(draw_hover ? hc->location() : gfx::Point())); |
485 const SkColor hover_color = | 495 const SkColor hover_color = |
486 SkColorSetA(params.toolbar_color, draw_hover ? hc->GetAlpha() : 255); | 496 SkColorSetA(params.toolbar_color, draw_hover ? hc->GetAlpha() : 255); |
487 | 497 |
488 if (ui::MaterialDesignController::IsModeMaterial()) { | 498 if (ui::MaterialDesignController::IsModeMaterial()) { |
489 gfx::ScopedCanvas scoped_canvas(canvas); | 499 gfx::Path fill; |
490 const float scale = canvas->UndoDeviceScaleFactor(); | 500 SkPaint paint; |
| 501 paint.setAntiAlias(true); |
491 | 502 |
492 // Draw the fill. | 503 // Draw the fill. |
493 gfx::Path fill = GetFillPath(scale, rect.size()); | |
494 SkPaint paint; | |
495 paint.setAntiAlias(true); | |
496 { | 504 { |
497 gfx::ScopedCanvas clip_scoper(canvas); | 505 gfx::ScopedCanvas scoped_canvas(fill_canvas); |
498 canvas->ClipPath(fill, true); | 506 const float scale = fill_canvas->UndoDeviceScaleFactor(); |
499 if (!params.fill_image.isNull()) { | 507 |
500 gfx::ScopedCanvas scale_scoper(canvas); | 508 fill = GetFillPath(scale, rect.size()); |
501 canvas->sk_canvas()->scale(scale, scale); | 509 { |
502 canvas->TileImageInt(params.fill_image, rect.x(), rect.y(), 0, 0, | 510 gfx::ScopedCanvas clip_scoper(fill_canvas); |
503 rect.width(), rect.height()); | 511 fill_canvas->ClipPath(fill, true); |
504 } else { | 512 if (!params.fill_image.isNull()) { |
505 paint.setColor(params.is_active ? params.toolbar_color | 513 gfx::ScopedCanvas scale_scoper(fill_canvas); |
506 : params.background_tab_color); | 514 fill_canvas->sk_canvas()->scale(scale, scale); |
507 canvas->DrawRect( | 515 fill_canvas->TileImageInt(params.fill_image, rect.x(), rect.y(), 0, 0, |
508 gfx::ScaleToEnclosingRect(gfx::Rect(rect.size()), scale), paint); | 516 rect.width(), rect.height()); |
509 } | 517 } else { |
510 if (draw_hover) { | 518 paint.setColor(params.is_active ? params.toolbar_color |
511 hover_location.scale(SkFloatToScalar(scale)); | 519 : params.background_tab_color); |
512 DrawHighlight(canvas, hover_location, radius * scale, hover_color); | 520 fill_canvas->DrawRect( |
| 521 gfx::ScaleToEnclosingRect(gfx::Rect(rect.size()), scale), |
| 522 paint); |
| 523 } |
| 524 if (draw_hover) { |
| 525 hover_location.scale(SkFloatToScalar(scale)); |
| 526 DrawHighlight(fill_canvas, hover_location, radius * scale, |
| 527 hover_color); |
| 528 } |
513 } | 529 } |
514 } | 530 } |
515 | 531 |
516 // Draw the stroke. | 532 // Draw the stroke. |
517 gfx::Path stroke = GetBorderPath(scale, false, false, rect.size()); | 533 { |
518 Op(stroke, fill, kDifference_SkPathOp, &stroke); | 534 gfx::ScopedCanvas scoped_canvas(stroke_canvas); |
519 if (!params.is_active) { | 535 const float scale = stroke_canvas->UndoDeviceScaleFactor(); |
520 // Clip out the bottom line; this will be drawn for us by | 536 |
521 // TabStrip::PaintChildren(). | 537 gfx::Path stroke = GetBorderPath(scale, false, false, rect.size()); |
522 canvas->ClipRect(gfx::RectF(rect.width() * scale, | 538 Op(stroke, fill, kDifference_SkPathOp, &stroke); |
523 rect.height() * scale - 1)); | 539 if (!params.is_active) { |
| 540 // Clip out the bottom line; this will be drawn for us by |
| 541 // TabStrip::PaintChildren(). |
| 542 stroke_canvas->ClipRect(gfx::RectF(rect.width() * scale, |
| 543 rect.height() * scale - 1)); |
| 544 } |
| 545 paint.setColor(params.stroke_color); |
| 546 stroke_canvas->DrawPath(stroke, paint); |
524 } | 547 } |
525 paint.setColor(params.stroke_color); | |
526 canvas->DrawPath(stroke, paint); | |
527 } else { | 548 } else { |
| 549 gfx::Canvas* canvas = stroke_canvas; |
528 if (draw_hover) { | 550 if (draw_hover) { |
529 // Draw everything to a temporary canvas so we can extract an image for | 551 // Draw everything to a temporary canvas so we can extract an image for |
530 // use in masking the hover glow. | 552 // use in masking the hover glow. |
531 gfx::Canvas background_canvas(rect.size(), canvas->image_scale(), false); | 553 gfx::Canvas background_canvas(rect.size(), canvas->image_scale(), false); |
532 PaintTabFill(&background_canvas, params.fill_image, rect, | 554 PaintTabFill(&background_canvas, params.fill_image, rect, |
533 params.is_active); | 555 params.is_active); |
534 gfx::ImageSkia background_image(background_canvas.ExtractImageRep()); | 556 gfx::ImageSkia background_image(background_canvas.ExtractImageRep()); |
535 canvas->DrawImageInt(background_image, 0, 0); | 557 canvas->DrawImageInt(background_image, 0, 0); |
536 | 558 |
537 gfx::Canvas hover_canvas(rect.size(), canvas->image_scale(), false); | 559 gfx::Canvas hover_canvas(rect.size(), canvas->image_scale(), false); |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
645 void OnGestureEvent(ui::GestureEvent* event) override { | 667 void OnGestureEvent(ui::GestureEvent* event) override { |
646 // Consume all gesture events here so that the parent (Tab) does not | 668 // Consume all gesture events here so that the parent (Tab) does not |
647 // start consuming gestures. | 669 // start consuming gestures. |
648 ImageButton::OnGestureEvent(event); | 670 ImageButton::OnGestureEvent(event); |
649 event->SetHandled(); | 671 event->SetHandled(); |
650 } | 672 } |
651 | 673 |
652 const char* GetClassName() const override { return kTabCloseButtonName; } | 674 const char* GetClassName() const override { return kTabCloseButtonName; } |
653 | 675 |
654 private: | 676 private: |
655 // Returns the rectangular bounds of parent tab's visible region in the | |
656 // local coordinate space of |this|. | |
657 gfx::Rect GetTabBounds() const { | |
658 gfx::Path tab_mask; | |
659 tab_->GetHitTestMask(&tab_mask); | |
660 | |
661 gfx::RectF tab_bounds_f(gfx::SkRectToRectF(tab_mask.getBounds())); | |
662 views::View::ConvertRectToTarget(tab_, this, &tab_bounds_f); | |
663 return gfx::ToEnclosingRect(tab_bounds_f); | |
664 } | |
665 | |
666 // Returns the rectangular bounds of the tab close button in the local | |
667 // coordinate space of |this|, not including clipped regions on the top | |
668 // or bottom of the button. |tab_bounds| is the rectangular bounds of | |
669 // the parent tab's visible region in the local coordinate space of |this|. | |
670 gfx::Rect GetTabCloseButtonBounds(const gfx::Rect& tab_bounds) const { | |
671 gfx::Rect button_bounds(GetContentsBounds()); | |
672 button_bounds.set_x(GetMirroredXForRect(button_bounds)); | |
673 | |
674 int top_overflow = tab_bounds.y() - button_bounds.y(); | |
675 int bottom_overflow = button_bounds.bottom() - tab_bounds.bottom(); | |
676 if (top_overflow > 0) | |
677 button_bounds.set_y(tab_bounds.y()); | |
678 else if (bottom_overflow > 0) | |
679 button_bounds.set_height(button_bounds.height() - bottom_overflow); | |
680 | |
681 return button_bounds; | |
682 } | |
683 | |
684 // views::MaskedTargeterDelegate: | 677 // views::MaskedTargeterDelegate: |
685 View* TargetForRect(View* root, const gfx::Rect& rect) override { | 678 View* TargetForRect(View* root, const gfx::Rect& rect) override { |
686 CHECK_EQ(root, this); | 679 CHECK_EQ(root, this); |
687 | 680 |
688 if (!views::UsePointBasedTargeting(rect)) | 681 if (!views::UsePointBasedTargeting(rect)) |
689 return ViewTargeterDelegate::TargetForRect(root, rect); | 682 return ViewTargeterDelegate::TargetForRect(root, rect); |
690 | 683 |
691 // Ignore the padding set on the button. | 684 // Ignore the padding set on the button. |
692 gfx::Rect contents_bounds = GetContentsBounds(); | 685 gfx::Rect contents_bounds = GetContentsBounds(); |
693 contents_bounds.set_x(GetMirroredXForRect(contents_bounds)); | 686 contents_bounds.set_x(GetMirroredXForRect(contents_bounds)); |
(...skipping 10 matching lines...) Expand all Loading... |
704 // as it will be pointless. | 697 // as it will be pointless. |
705 if (aura::Env::GetInstance()->is_touch_down()) | 698 if (aura::Env::GetInstance()->is_touch_down()) |
706 contents_bounds = GetLocalBounds(); | 699 contents_bounds = GetLocalBounds(); |
707 #endif | 700 #endif |
708 | 701 |
709 return contents_bounds.Intersects(rect) ? this : parent(); | 702 return contents_bounds.Intersects(rect) ? this : parent(); |
710 } | 703 } |
711 | 704 |
712 // We need to define this so hit-testing won't include the border region. | 705 // We need to define this so hit-testing won't include the border region. |
713 bool GetHitTestMask(gfx::Path* mask) const override { | 706 bool GetHitTestMask(gfx::Path* mask) const override { |
714 DCHECK(mask); | 707 gfx::Rect button_bounds(GetContentsBounds()); |
715 mask->reset(); | 708 button_bounds.set_x(GetMirroredXForRect(button_bounds)); |
716 | 709 mask->addRect(gfx::RectToSkRect(button_bounds)); |
717 // The parent tab may be partially occluded by another tab if we are | 710 return true; |
718 // in stacked tab mode, which means that the tab close button may also | |
719 // be partially occluded. Define the hit test mask of the tab close | |
720 // button to be the intersection of the parent tab's visible bounds | |
721 // and the bounds of the tab close button. | |
722 gfx::Rect tab_bounds(GetTabBounds()); | |
723 gfx::Rect button_bounds(GetTabCloseButtonBounds(tab_bounds)); | |
724 gfx::Rect intersection(gfx::IntersectRects(tab_bounds, button_bounds)); | |
725 | |
726 if (!intersection.IsEmpty()) { | |
727 mask->addRect(RectToSkRect(intersection)); | |
728 return true; | |
729 } | |
730 | |
731 return false; | |
732 } | |
733 | |
734 bool DoesIntersectRect(const View* target, | |
735 const gfx::Rect& rect) const override { | |
736 CHECK_EQ(target, this); | |
737 | |
738 // If the request is not made in response to a gesture, use the | |
739 // default implementation. | |
740 if (views::UsePointBasedTargeting(rect)) | |
741 return MaskedTargeterDelegate::DoesIntersectRect(target, rect); | |
742 | |
743 // The hit test request is in response to a gesture. Return false if any | |
744 // part of the tab close button is hidden from the user. | |
745 // TODO(tdanderson): Consider always returning the intersection if the | |
746 // non-rectangular shape of the tab can be accounted for. | |
747 gfx::Rect tab_bounds(GetTabBounds()); | |
748 gfx::Rect button_bounds(GetTabCloseButtonBounds(tab_bounds)); | |
749 if (!tab_bounds.Contains(button_bounds)) | |
750 return false; | |
751 | |
752 return MaskedTargeterDelegate::DoesIntersectRect(target, rect); | |
753 } | 711 } |
754 | 712 |
755 Tab* tab_; | 713 Tab* tab_; |
756 | 714 |
757 DISALLOW_COPY_AND_ASSIGN(TabCloseButton); | 715 DISALLOW_COPY_AND_ASSIGN(TabCloseButton); |
758 }; | 716 }; |
759 | 717 |
760 //////////////////////////////////////////////////////////////////////////////// | 718 //////////////////////////////////////////////////////////////////////////////// |
761 // ThrobberView | 719 // ThrobberView |
762 // | 720 // |
(...skipping 367 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1130 const gfx::Point& point, | 1088 const gfx::Point& point, |
1131 ui::MenuSourceType source_type) { | 1089 ui::MenuSourceType source_type) { |
1132 if (!closing()) | 1090 if (!closing()) |
1133 controller_->ShowContextMenuForTab(this, point, source_type); | 1091 controller_->ShowContextMenuForTab(this, point, source_type); |
1134 } | 1092 } |
1135 | 1093 |
1136 //////////////////////////////////////////////////////////////////////////////// | 1094 //////////////////////////////////////////////////////////////////////////////// |
1137 // Tab, views::MaskedTargeterDelegate overrides: | 1095 // Tab, views::MaskedTargeterDelegate overrides: |
1138 | 1096 |
1139 bool Tab::GetHitTestMask(gfx::Path* mask) const { | 1097 bool Tab::GetHitTestMask(gfx::Path* mask) const { |
1140 const float scale = GetWidget()->GetCompositor()->device_scale_factor(); | |
1141 // When the window is maximized we don't want to shave off the edges or top | 1098 // When the window is maximized we don't want to shave off the edges or top |
1142 // shadow of the tab, such that the user can click anywhere along the top | 1099 // shadow of the tab, such that the user can click anywhere along the top |
1143 // edge of the screen to select a tab. Ditto for immersive fullscreen. | 1100 // edge of the screen to select a tab. Ditto for immersive fullscreen. |
1144 const views::Widget* widget = GetWidget(); | 1101 const views::Widget* widget = GetWidget(); |
1145 const bool extend_to_top = | 1102 *mask = GetBorderPath( |
1146 widget && (widget->IsMaximized() || widget->IsFullscreen()); | 1103 GetWidget()->GetCompositor()->device_scale_factor(), true, |
1147 *mask = GetBorderPath(scale, true, extend_to_top, size()); | 1104 widget && (widget->IsMaximized() || widget->IsFullscreen()), size()); |
1148 | |
1149 // It is possible for a portion of the tab to be occluded if tabs are | |
1150 // stacked, so modify the hit test mask to only include the visible | |
1151 // region of the tab. | |
1152 gfx::Rect clip; | |
1153 controller_->ShouldPaintTab(this, &clip); | |
1154 if (clip.size().GetArea()) { | |
1155 SkRect intersection(mask->getBounds()); | |
1156 mask->reset(); | |
1157 if (!intersection.intersect(RectToSkRect(clip))) | |
1158 return false; | |
1159 mask->addRect(intersection); | |
1160 } | |
1161 return true; | 1105 return true; |
1162 } | 1106 } |
1163 | 1107 |
1164 //////////////////////////////////////////////////////////////////////////////// | 1108 //////////////////////////////////////////////////////////////////////////////// |
1165 // Tab, views::View overrides: | 1109 // Tab, views::View overrides: |
1166 | 1110 |
1167 void Tab::ViewHierarchyChanged(const ViewHierarchyChangedDetails& details) { | 1111 void Tab::ViewHierarchyChanged(const ViewHierarchyChangedDetails& details) { |
1168 // If this hierarchy changed has resulted in us being part of a widget | 1112 // If this hierarchy changed has resulted in us being part of a widget |
1169 // hierarchy for the first time, we can now get at the theme provider, and | 1113 // hierarchy for the first time, we can now get at the theme provider, and |
1170 // should recalculate the button color. | 1114 // should recalculate the button color. |
1171 if (details.is_add) | 1115 if (details.is_add) |
1172 OnButtonColorMaybeChanged(); | 1116 OnButtonColorMaybeChanged(); |
1173 } | 1117 } |
1174 | 1118 |
1175 void Tab::OnPaint(gfx::Canvas* canvas) { | 1119 void Tab::OnPaint(gfx::Canvas* canvas) { |
1176 // Don't paint if we're narrower than we can render correctly. (This should | 1120 // Don't paint if we're narrower than we can render correctly. (This should |
1177 // only happen during animations). | 1121 // only happen during animations). |
1178 if (width() < GetMinimumInactiveSize().width() && !data().pinned) | 1122 if (width() < GetMinimumInactiveSize().width() && !data().pinned) |
1179 return; | 1123 return; |
1180 | 1124 |
1181 gfx::Rect clip; | 1125 gfx::Path clip; |
1182 if (!controller_->ShouldPaintTab(this, &clip)) | 1126 if (!controller_->ShouldPaintTab( |
| 1127 this, base::Bind(&GetBorderPath, canvas->image_scale(), true, false), |
| 1128 &clip)) |
1183 return; | 1129 return; |
1184 if (!clip.IsEmpty()) { | |
1185 canvas->Save(); | |
1186 canvas->ClipRect(clip); | |
1187 } | |
1188 | 1130 |
1189 if (controller_->IsImmersiveStyle()) | 1131 if (controller_->IsImmersiveStyle()) |
1190 PaintImmersiveTab(canvas); | 1132 PaintImmersiveTab(canvas); |
1191 else | 1133 else |
1192 PaintTab(canvas); | 1134 PaintTab(canvas, clip); |
1193 | |
1194 if (!clip.IsEmpty()) | |
1195 canvas->Restore(); | |
1196 } | 1135 } |
1197 | 1136 |
1198 void Tab::Layout() { | 1137 void Tab::Layout() { |
1199 const gfx::Rect lb = GetContentsBounds(); | 1138 const gfx::Rect lb = GetContentsBounds(); |
1200 showing_icon_ = ShouldShowIcon(); | 1139 showing_icon_ = ShouldShowIcon(); |
1201 // See comments in IconCapacity(). | 1140 // See comments in IconCapacity(). |
1202 const int extra_padding = | 1141 const int extra_padding = |
1203 (controller_->ShouldHideCloseButtonForInactiveTabs() || | 1142 (controller_->ShouldHideCloseButtonForInactiveTabs() || |
1204 (IconCapacity() < 3)) ? 0 : kExtraLeftPaddingToBalanceCloseButtonPadding; | 1143 (IconCapacity() < 3)) ? 0 : kExtraLeftPaddingToBalanceCloseButtonPadding; |
1205 const int start = lb.x() + extra_padding; | 1144 const int start = lb.x() + extra_padding; |
(...skipping 268 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1474 | 1413 |
1475 if (data().blocked == old.blocked) | 1414 if (data().blocked == old.blocked) |
1476 return; | 1415 return; |
1477 | 1416 |
1478 if (data().blocked) | 1417 if (data().blocked) |
1479 StartPulse(); | 1418 StartPulse(); |
1480 else | 1419 else |
1481 StopPulse(); | 1420 StopPulse(); |
1482 } | 1421 } |
1483 | 1422 |
1484 void Tab::PaintTab(gfx::Canvas* canvas) { | 1423 void Tab::PaintTab(gfx::Canvas* canvas, const gfx::Path& clip) { |
1485 const int kActiveTabFillId = IDR_THEME_TOOLBAR; | 1424 const int kActiveTabFillId = IDR_THEME_TOOLBAR; |
1486 const bool has_custom_image = | 1425 const bool has_custom_image = |
1487 GetThemeProvider()->HasCustomImage(kActiveTabFillId); | 1426 GetThemeProvider()->HasCustomImage(kActiveTabFillId); |
1488 const int y_offset = -GetYInsetForActiveTabBackground(); | 1427 const int y_offset = -GetYInsetForActiveTabBackground(); |
1489 if (IsActive()) { | 1428 if (IsActive()) { |
1490 PaintTabBackgroundUsingFillId(canvas, true, kActiveTabFillId, | 1429 PaintTabBackgroundUsingFillId(canvas, canvas, true, kActiveTabFillId, |
1491 has_custom_image, y_offset); | 1430 has_custom_image, y_offset); |
1492 } else { | 1431 } else { |
1493 PaintInactiveTabBackground(canvas); | 1432 PaintInactiveTabBackground(canvas, clip); |
1494 | 1433 |
1495 const double throb_value = GetThrobValue(); | 1434 const double throb_value = GetThrobValue(); |
1496 if (throb_value > 0) { | 1435 if (throb_value > 0) { |
1497 canvas->SaveLayerAlpha(gfx::ToRoundedInt(throb_value * 0xff), | 1436 canvas->SaveLayerAlpha(gfx::ToRoundedInt(throb_value * 0xff), |
1498 GetLocalBounds()); | 1437 GetLocalBounds()); |
1499 PaintTabBackgroundUsingFillId(canvas, true, kActiveTabFillId, | 1438 PaintTabBackgroundUsingFillId(canvas, canvas, true, kActiveTabFillId, |
1500 has_custom_image, y_offset); | 1439 has_custom_image, y_offset); |
1501 canvas->Restore(); | 1440 canvas->Restore(); |
1502 } | 1441 } |
1503 } | 1442 } |
1504 | 1443 |
1505 if (showing_icon_) | 1444 if (showing_icon_) |
1506 PaintIcon(canvas); | 1445 PaintIcon(canvas); |
1507 } | 1446 } |
1508 | 1447 |
1509 void Tab::PaintImmersiveTab(gfx::Canvas* canvas) { | 1448 void Tab::PaintImmersiveTab(gfx::Canvas* canvas) { |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1543 canvas->FillRect(right_eye_rect, kEyeColor); | 1482 canvas->FillRect(right_eye_rect, kEyeColor); |
1544 // The second part parts the remaining |eye_width| on the left. | 1483 // The second part parts the remaining |eye_width| on the left. |
1545 int left_eye_width = eye_offset + eye_width - bar_rect.width(); | 1484 int left_eye_width = eye_offset + eye_width - bar_rect.width(); |
1546 gfx::Rect left_eye_rect( | 1485 gfx::Rect left_eye_rect( |
1547 bar_rect.x(), 0, left_eye_width, kImmersiveBarHeight); | 1486 bar_rect.x(), 0, left_eye_width, kImmersiveBarHeight); |
1548 canvas->FillRect(left_eye_rect, kEyeColor); | 1487 canvas->FillRect(left_eye_rect, kEyeColor); |
1549 } | 1488 } |
1550 } | 1489 } |
1551 } | 1490 } |
1552 | 1491 |
1553 void Tab::PaintInactiveTabBackground(gfx::Canvas* canvas) { | 1492 void Tab::PaintInactiveTabBackground(gfx::Canvas* canvas, |
| 1493 const gfx::Path& clip) { |
1554 bool has_custom_image; | 1494 bool has_custom_image; |
1555 int fill_id = controller_->GetBackgroundResourceId(&has_custom_image); | 1495 int fill_id = controller_->GetBackgroundResourceId(&has_custom_image); |
1556 | 1496 |
1557 // If the theme is providing a custom background image, then its top edge | 1497 // If the theme is providing a custom background image, then its top edge |
1558 // should be at the top of the tab. Otherwise, we assume that the background | 1498 // should be at the top of the tab. Otherwise, we assume that the background |
1559 // image is a composited foreground + frame image. Note that if the theme is | 1499 // image is a composited foreground + frame image. Note that if the theme is |
1560 // only providing a custom frame image, |has_custom_image| will be true, but | 1500 // only providing a custom frame image, |has_custom_image| will be true, but |
1561 // we should use the |background_offset_| here. | 1501 // we should use the |background_offset_| here. |
1562 const ui::ThemeProvider* tp = GetThemeProvider(); | 1502 const ui::ThemeProvider* tp = GetThemeProvider(); |
1563 const int y_offset = tp->HasCustomImage(fill_id) ? | 1503 const int y_offset = tp->HasCustomImage(fill_id) ? |
1564 -GetLayoutConstant(TAB_TOP_EXCLUSION_HEIGHT) : background_offset_.y(); | 1504 -GetLayoutConstant(TAB_TOP_EXCLUSION_HEIGHT) : background_offset_.y(); |
1565 | 1505 |
1566 // We only cache the image when it's the default image and we're not hovered, | 1506 // We only cache the image when it's the default image and we're not hovered, |
1567 // to avoid caching a background image that isn't the same for all tabs. | 1507 // to avoid caching a background image that isn't the same for all tabs. |
1568 if (has_custom_image || hover_controller_.ShouldDraw()) { | 1508 if (has_custom_image || hover_controller_.ShouldDraw()) { |
1569 PaintTabBackgroundUsingFillId(canvas, false, fill_id, has_custom_image, | 1509 PaintTabBackgroundUsingFillId(canvas, canvas, false, fill_id, |
1570 y_offset); | 1510 has_custom_image, y_offset); |
1571 return; | 1511 return; |
1572 } | 1512 } |
1573 | 1513 |
| 1514 // For efficiency, we don't use separate fill and stroke images unless we |
| 1515 // really need to clip the stroke and not the fill (for stacked tabs). This |
| 1516 // saves memory and avoids an extra image draw at the cost of recalculating |
| 1517 // the images when MaySetClip() toggles. |
| 1518 const bool use_fill_and_stroke_images = |
| 1519 controller_->MaySetClip() && |
| 1520 ui::MaterialDesignController::IsModeMaterial(); |
| 1521 |
1574 const ImageCacheEntryMetadata metadata( | 1522 const ImageCacheEntryMetadata metadata( |
1575 fill_id, tp->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB), | 1523 fill_id, tp->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB), |
1576 controller_->GetToolbarTopSeparatorColor(), | 1524 controller_->GetToolbarTopSeparatorColor(), use_fill_and_stroke_images, |
1577 ui::GetSupportedScaleFactor(canvas->image_scale()), size()); | 1525 ui::GetSupportedScaleFactor(canvas->image_scale()), size()); |
1578 auto it = std::find_if( | 1526 auto it = std::find_if( |
1579 g_image_cache->begin(), g_image_cache->end(), | 1527 g_image_cache->begin(), g_image_cache->end(), |
1580 [&metadata](const ImageCacheEntry& e) { return e.metadata == metadata; }); | 1528 [&metadata](const ImageCacheEntry& e) { return e.metadata == metadata; }); |
1581 if (it == g_image_cache->end()) { | 1529 if (it == g_image_cache->end()) { |
1582 gfx::Canvas tmp_canvas(size(), canvas->image_scale(), false); | 1530 gfx::Canvas tmp_canvas(size(), canvas->image_scale(), false); |
1583 PaintTabBackgroundUsingFillId(&tmp_canvas, false, fill_id, false, y_offset); | 1531 if (use_fill_and_stroke_images) { |
1584 g_image_cache->emplace_front(metadata, | 1532 gfx::Canvas tmp_fill_canvas(size(), canvas->image_scale(), false); |
1585 gfx::ImageSkia(tmp_canvas.ExtractImageRep())); | 1533 PaintTabBackgroundUsingFillId(&tmp_fill_canvas, &tmp_canvas, false, |
| 1534 fill_id, false, y_offset); |
| 1535 g_image_cache->emplace_front( |
| 1536 metadata, gfx::ImageSkia(tmp_fill_canvas.ExtractImageRep()), |
| 1537 gfx::ImageSkia(tmp_canvas.ExtractImageRep())); |
| 1538 } else { |
| 1539 PaintTabBackgroundUsingFillId(&tmp_canvas, &tmp_canvas, false, fill_id, |
| 1540 false, y_offset); |
| 1541 g_image_cache->emplace_front( |
| 1542 metadata, gfx::ImageSkia(), |
| 1543 gfx::ImageSkia(tmp_canvas.ExtractImageRep())); |
| 1544 } |
1586 if (g_image_cache->size() > kMaxImageCacheSize) | 1545 if (g_image_cache->size() > kMaxImageCacheSize) |
1587 g_image_cache->pop_back(); | 1546 g_image_cache->pop_back(); |
1588 it = g_image_cache->begin(); | 1547 it = g_image_cache->begin(); |
1589 } | 1548 } |
1590 canvas->DrawImageInt(it->image, 0, 0); | 1549 |
| 1550 gfx::ScopedCanvas scoped_canvas( |
| 1551 use_fill_and_stroke_images ? canvas : nullptr); |
| 1552 if (use_fill_and_stroke_images) { |
| 1553 canvas->DrawImageInt(it->fill_image, 0, 0); |
| 1554 canvas->sk_canvas()->clipPath(clip, SkRegion::kDifference_Op, true); |
| 1555 } |
| 1556 canvas->DrawImageInt(it->stroke_image, 0, 0); |
1591 } | 1557 } |
1592 | 1558 |
1593 void Tab::PaintTabBackgroundUsingFillId(gfx::Canvas* canvas, | 1559 void Tab::PaintTabBackgroundUsingFillId(gfx::Canvas* fill_canvas, |
| 1560 gfx::Canvas* stroke_canvas, |
1594 bool is_active, | 1561 bool is_active, |
1595 int fill_id, | 1562 int fill_id, |
1596 bool has_custom_image, | 1563 bool has_custom_image, |
1597 int y_offset) { | 1564 int y_offset) { |
1598 views::GlowHoverController* hc = | 1565 views::GlowHoverController* hc = |
1599 hover_controller_.ShouldDraw() ? &hover_controller_ : nullptr; | 1566 hover_controller_.ShouldDraw() ? &hover_controller_ : nullptr; |
1600 gfx::ImageSkia* fill_image = | 1567 gfx::ImageSkia* fill_image = |
1601 has_custom_image || !ui::MaterialDesignController::IsModeMaterial() | 1568 has_custom_image || !ui::MaterialDesignController::IsModeMaterial() |
1602 ? GetThemeProvider()->GetImageSkiaNamed(fill_id) | 1569 ? GetThemeProvider()->GetImageSkiaNamed(fill_id) |
1603 : nullptr; | 1570 : nullptr; |
1604 // The tab image needs to be lined up with the background image | 1571 // The tab image needs to be lined up with the background image |
1605 // so that it feels partially transparent. These offsets represent the tab | 1572 // so that it feels partially transparent. These offsets represent the tab |
1606 // position within the frame background image. | 1573 // position within the frame background image. |
1607 gfx::Rect rect(GetLocalBounds()); | 1574 gfx::Rect rect(GetLocalBounds()); |
1608 rect.Offset(GetMirroredX() + background_offset_.x(), y_offset); | 1575 rect.Offset(GetMirroredX() + background_offset_.x(), y_offset); |
1609 PaintBackgroundParams params( | 1576 PaintBackgroundParams params( |
1610 is_active, fill_image, has_custom_image, rect, | 1577 is_active, fill_image, has_custom_image, rect, |
1611 controller_->GetToolbarTopSeparatorColor(), | 1578 controller_->GetToolbarTopSeparatorColor(), |
1612 GetThemeProvider()->GetColor(ThemeProperties::COLOR_TOOLBAR), | 1579 GetThemeProvider()->GetColor(ThemeProperties::COLOR_TOOLBAR), |
1613 GetThemeProvider()->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB)); | 1580 GetThemeProvider()->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB)); |
1614 | 1581 |
1615 PaintTabBackgroundUsingParams(canvas, hc, params); | 1582 PaintTabBackgroundUsingParams(fill_canvas, stroke_canvas, hc, params); |
1616 } | 1583 } |
1617 | 1584 |
1618 void Tab::PaintPinnedTabTitleChangedIndicatorAndIcon( | 1585 void Tab::PaintPinnedTabTitleChangedIndicatorAndIcon( |
1619 gfx::Canvas* canvas, | 1586 gfx::Canvas* canvas, |
1620 const gfx::Rect& favicon_draw_bounds) { | 1587 const gfx::Rect& favicon_draw_bounds) { |
1621 // The pinned tab title changed indicator consists of two parts: | 1588 // The pinned tab title changed indicator consists of two parts: |
1622 // . a clear (totally transparent) part over the bottom right (or left in rtl) | 1589 // . a clear (totally transparent) part over the bottom right (or left in rtl) |
1623 // of the favicon. This is done by drawing the favicon to a canvas, then | 1590 // of the favicon. This is done by drawing the favicon to a canvas, then |
1624 // drawing the clear part on top of the favicon. | 1591 // drawing the clear part on top of the favicon. |
1625 // . a circle in the bottom right (or left in rtl) of the favicon. | 1592 // . a circle in the bottom right (or left in rtl) of the favicon. |
(...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1854 gfx::Rect Tab::GetImmersiveBarRect() const { | 1821 gfx::Rect Tab::GetImmersiveBarRect() const { |
1855 // The main bar is as wide as the normal tab's horizontal top line. | 1822 // The main bar is as wide as the normal tab's horizontal top line. |
1856 // This top line of the tab extends a few pixels left and right of the | 1823 // This top line of the tab extends a few pixels left and right of the |
1857 // center image due to pixels in the rounded corner images. | 1824 // center image due to pixels in the rounded corner images. |
1858 const int kBarPadding = 1; | 1825 const int kBarPadding = 1; |
1859 int main_bar_left = g_active_images.l_width - kBarPadding; | 1826 int main_bar_left = g_active_images.l_width - kBarPadding; |
1860 int main_bar_right = width() - g_active_images.r_width + kBarPadding; | 1827 int main_bar_right = width() - g_active_images.r_width + kBarPadding; |
1861 return gfx::Rect( | 1828 return gfx::Rect( |
1862 main_bar_left, 0, main_bar_right - main_bar_left, kImmersiveBarHeight); | 1829 main_bar_left, 0, main_bar_right - main_bar_left, kImmersiveBarHeight); |
1863 } | 1830 } |
OLD | NEW |