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 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
117 const char kTabCloseButtonName[] = "TabCloseButton"; | 117 const char kTabCloseButtonName[] = "TabCloseButton"; |
118 | 118 |
119 //////////////////////////////////////////////////////////////////////////////// | 119 //////////////////////////////////////////////////////////////////////////////// |
120 // ImageCacheEntryMetadata | 120 // ImageCacheEntryMetadata |
121 // | 121 // |
122 // All metadata necessary to uniquely identify a cached image. | 122 // All metadata necessary to uniquely identify a cached image. |
123 struct ImageCacheEntryMetadata { | 123 struct ImageCacheEntryMetadata { |
124 ImageCacheEntryMetadata(int resource_id, | 124 ImageCacheEntryMetadata(int resource_id, |
125 SkColor fill_color, | 125 SkColor fill_color, |
126 SkColor stroke_color, | 126 SkColor stroke_color, |
127 bool use_fill_and_stroke_images, | |
127 ui::ScaleFactor scale_factor, | 128 ui::ScaleFactor scale_factor, |
128 const gfx::Size& size); | 129 const gfx::Size& size); |
129 | 130 |
130 ~ImageCacheEntryMetadata(); | 131 ~ImageCacheEntryMetadata(); |
131 | 132 |
132 bool operator==(const ImageCacheEntryMetadata& rhs) const; | 133 bool operator==(const ImageCacheEntryMetadata& rhs) const; |
133 | 134 |
134 int resource_id; // Only needed by pre-MD | 135 int resource_id; // Only needed by pre-MD |
135 SkColor fill_color; // Both colors only needed by MD | 136 SkColor fill_color; // Both colors only needed by MD |
136 SkColor stroke_color; | 137 SkColor stroke_color; |
138 bool use_fill_and_stroke_images; | |
137 ui::ScaleFactor scale_factor; | 139 ui::ScaleFactor scale_factor; |
138 gfx::Size size; | 140 gfx::Size size; |
139 }; | 141 }; |
140 | 142 |
141 ImageCacheEntryMetadata::ImageCacheEntryMetadata(int resource_id, | 143 ImageCacheEntryMetadata::ImageCacheEntryMetadata( |
142 SkColor fill_color, | 144 int resource_id, |
143 SkColor stroke_color, | 145 SkColor fill_color, |
144 ui::ScaleFactor scale_factor, | 146 SkColor stroke_color, |
145 const gfx::Size& size) | 147 bool use_fill_and_stroke_images, |
148 ui::ScaleFactor scale_factor, | |
149 const gfx::Size& size) | |
146 : resource_id(resource_id), | 150 : resource_id(resource_id), |
147 fill_color(fill_color), | 151 fill_color(fill_color), |
148 stroke_color(stroke_color), | 152 stroke_color(stroke_color), |
153 use_fill_and_stroke_images(use_fill_and_stroke_images), | |
149 scale_factor(scale_factor), | 154 scale_factor(scale_factor), |
150 size(size) { | 155 size(size) { |
151 DCHECK_NE(ui::SCALE_FACTOR_NONE, scale_factor); | 156 DCHECK_NE(ui::SCALE_FACTOR_NONE, scale_factor); |
152 | 157 |
153 // Some fields are only relevant for pre-MD vs. MD. Erase the irrelevant ones | 158 // Some fields are only relevant for pre-MD vs. MD. Erase the irrelevant ones |
154 // so they don't cause incorrect cache misses. | 159 // so they don't cause incorrect cache misses. |
155 // TODO(pkasting): Remove |resource_id| field when non-MD code is deleted. | 160 // TODO(pkasting): Remove |resource_id| field when non-MD code is deleted. |
156 if (ui::MaterialDesignController::IsModeMaterial()) | 161 if (ui::MaterialDesignController::IsModeMaterial()) |
157 resource_id = 0; | 162 resource_id = 0; |
158 else | 163 else |
159 fill_color = stroke_color = SK_ColorTRANSPARENT; | 164 fill_color = stroke_color = SK_ColorTRANSPARENT; |
160 } | 165 } |
161 | 166 |
162 ImageCacheEntryMetadata::~ImageCacheEntryMetadata() {} | 167 ImageCacheEntryMetadata::~ImageCacheEntryMetadata() {} |
163 | 168 |
164 bool ImageCacheEntryMetadata::operator==( | 169 bool ImageCacheEntryMetadata::operator==( |
165 const ImageCacheEntryMetadata& rhs) const { | 170 const ImageCacheEntryMetadata& rhs) const { |
166 return resource_id == rhs.resource_id && fill_color == rhs.fill_color && | 171 return resource_id == rhs.resource_id && fill_color == rhs.fill_color && |
167 stroke_color == rhs.stroke_color && scale_factor == rhs.scale_factor && | 172 stroke_color == rhs.stroke_color && |
168 size == rhs.size; | 173 use_fill_and_stroke_images == rhs.use_fill_and_stroke_images && |
174 scale_factor == rhs.scale_factor && size == rhs.size; | |
169 } | 175 } |
170 | 176 |
171 //////////////////////////////////////////////////////////////////////////////// | 177 //////////////////////////////////////////////////////////////////////////////// |
172 // ImageCacheEntry and cache management | 178 // ImageCacheEntry and cache management |
173 // | 179 // |
174 // A cached image and the metadata used to generate it. | 180 // A cached image and the metadata used to generate it. |
175 struct ImageCacheEntry { | 181 struct ImageCacheEntry { |
176 ImageCacheEntry(const ImageCacheEntryMetadata& metadata, | 182 ImageCacheEntry(const ImageCacheEntryMetadata& metadata, |
177 const gfx::ImageSkia& image); | 183 const gfx::ImageSkia& fill_image, |
184 const gfx::ImageSkia& stroke_image); | |
178 ~ImageCacheEntry(); | 185 ~ImageCacheEntry(); |
179 | 186 |
180 ImageCacheEntryMetadata metadata; | 187 ImageCacheEntryMetadata metadata; |
181 gfx::ImageSkia image; | 188 gfx::ImageSkia fill_image; |
189 gfx::ImageSkia stroke_image; | |
182 }; | 190 }; |
183 | 191 |
184 ImageCacheEntry::ImageCacheEntry(const ImageCacheEntryMetadata& metadata, | 192 ImageCacheEntry::ImageCacheEntry(const ImageCacheEntryMetadata& metadata, |
185 const gfx::ImageSkia& image) | 193 const gfx::ImageSkia& fill_image, |
186 : metadata(metadata), image(image) {} | 194 const gfx::ImageSkia& stroke_image) |
195 : metadata(metadata), fill_image(fill_image), stroke_image(stroke_image) {} | |
187 | 196 |
188 ImageCacheEntry::~ImageCacheEntry() {} | 197 ImageCacheEntry::~ImageCacheEntry() {} |
189 | 198 |
190 typedef std::list<ImageCacheEntry> ImageCache; | 199 typedef std::list<ImageCacheEntry> ImageCache; |
191 | 200 |
192 // As the majority of the tabs are inactive, and painting tabs is slowish, | 201 // As the majority of the tabs are inactive, and painting tabs is slowish, |
193 // we cache a handful of the inactive tab backgrounds here. | 202 // we cache a handful of the inactive tab backgrounds here. |
194 static ImageCache* g_image_cache = nullptr; | 203 static ImageCache* g_image_cache = nullptr; |
195 | 204 |
196 struct TabImages { | 205 struct TabImages { |
(...skipping 341 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
538 void OnGestureEvent(ui::GestureEvent* event) override { | 547 void OnGestureEvent(ui::GestureEvent* event) override { |
539 // Consume all gesture events here so that the parent (Tab) does not | 548 // Consume all gesture events here so that the parent (Tab) does not |
540 // start consuming gestures. | 549 // start consuming gestures. |
541 ImageButton::OnGestureEvent(event); | 550 ImageButton::OnGestureEvent(event); |
542 event->SetHandled(); | 551 event->SetHandled(); |
543 } | 552 } |
544 | 553 |
545 const char* GetClassName() const override { return kTabCloseButtonName; } | 554 const char* GetClassName() const override { return kTabCloseButtonName; } |
546 | 555 |
547 private: | 556 private: |
548 // Returns the rectangular bounds of parent tab's visible region in the | |
549 // local coordinate space of |this|. | |
550 gfx::Rect GetTabBounds() const { | |
551 gfx::Path tab_mask; | |
552 tab_->GetHitTestMask(&tab_mask); | |
553 | |
554 gfx::RectF tab_bounds_f(gfx::SkRectToRectF(tab_mask.getBounds())); | |
555 views::View::ConvertRectToTarget(tab_, this, &tab_bounds_f); | |
556 return gfx::ToEnclosingRect(tab_bounds_f); | |
557 } | |
558 | |
559 // Returns the rectangular bounds of the tab close button in the local | |
560 // coordinate space of |this|, not including clipped regions on the top | |
561 // or bottom of the button. |tab_bounds| is the rectangular bounds of | |
562 // the parent tab's visible region in the local coordinate space of |this|. | |
563 gfx::Rect GetTabCloseButtonBounds(const gfx::Rect& tab_bounds) const { | |
564 gfx::Rect button_bounds(GetContentsBounds()); | |
565 button_bounds.set_x(GetMirroredXForRect(button_bounds)); | |
566 | |
567 int top_overflow = tab_bounds.y() - button_bounds.y(); | |
568 int bottom_overflow = button_bounds.bottom() - tab_bounds.bottom(); | |
569 if (top_overflow > 0) | |
570 button_bounds.set_y(tab_bounds.y()); | |
571 else if (bottom_overflow > 0) | |
572 button_bounds.set_height(button_bounds.height() - bottom_overflow); | |
573 | |
574 return button_bounds; | |
575 } | |
576 | |
577 // views::MaskedTargeterDelegate: | 557 // views::MaskedTargeterDelegate: |
578 View* TargetForRect(View* root, const gfx::Rect& rect) override { | 558 View* TargetForRect(View* root, const gfx::Rect& rect) override { |
579 CHECK_EQ(root, this); | 559 CHECK_EQ(root, this); |
580 | 560 |
581 if (!views::UsePointBasedTargeting(rect)) | 561 if (!views::UsePointBasedTargeting(rect)) |
582 return ViewTargeterDelegate::TargetForRect(root, rect); | 562 return ViewTargeterDelegate::TargetForRect(root, rect); |
583 | 563 |
584 // Ignore the padding set on the button. | 564 // Ignore the padding set on the button. |
585 gfx::Rect contents_bounds = GetContentsBounds(); | 565 gfx::Rect contents_bounds = GetContentsBounds(); |
586 contents_bounds.set_x(GetMirroredXForRect(contents_bounds)); | 566 contents_bounds.set_x(GetMirroredXForRect(contents_bounds)); |
587 | 567 |
588 #if defined(USE_AURA) | 568 #if defined(USE_AURA) |
589 // Include the padding in hit-test for touch events. | 569 // Include the padding in hit-test for touch events. |
590 // TODO(pkasting): It seems like touch events would generate rects rather | 570 // TODO(pkasting): It seems like touch events would generate rects rather |
591 // than points and thus use the TargetForRect() call above. If this is | 571 // than points and thus use the TargetForRect() call above. If this is |
592 // reached, it may be from someone calling GetEventHandlerForPoint() while a | 572 // reached, it may be from someone calling GetEventHandlerForPoint() while a |
593 // touch happens to be occurring. In such a case, maybe we don't want this | 573 // touch happens to be occurring. In such a case, maybe we don't want this |
594 // code to run? It's possible this block should be removed, or maybe this | 574 // code to run? It's possible this block should be removed, or maybe this |
595 // whole function deleted. | 575 // whole function deleted. |
596 if (aura::Env::GetInstance()->is_touch_down()) | 576 if (aura::Env::GetInstance()->is_touch_down()) |
597 contents_bounds = GetLocalBounds(); | 577 contents_bounds = GetLocalBounds(); |
598 #endif | 578 #endif |
599 | 579 |
600 return contents_bounds.Intersects(rect) ? this : parent(); | 580 return contents_bounds.Intersects(rect) ? this : parent(); |
601 } | 581 } |
602 | 582 |
603 // We need to define this so hit-testing won't include the border region. | 583 // We need to define this so hit-testing won't include the border region. |
604 bool GetHitTestMask(gfx::Path* mask) const override { | 584 bool GetHitTestMask(gfx::Path* mask) const override { |
605 DCHECK(mask); | 585 gfx::Rect button_bounds(GetContentsBounds()); |
606 mask->reset(); | 586 button_bounds.set_x(GetMirroredXForRect(button_bounds)); |
587 mask->addRect(gfx::RectToSkRect(button_bounds)); | |
607 | 588 |
608 // The parent tab may be partially occluded by another tab if we are | 589 // Clip against the tab mask in case the close button is party occluded due |
609 // in stacked tab mode, which means that the tab close button may also | 590 // to stacking tabs. |
610 // be partially occluded. Define the hit test mask of the tab close | 591 // TODO(pkasting): Does this actually improve event targeting over using the |
611 // button to be the intersection of the parent tab's visible bounds | 592 // full close button bounds? |
612 // and the bounds of the tab close button. | 593 gfx::Path clip; |
613 gfx::Rect tab_bounds(GetTabBounds()); | 594 tab_->GetHitTestMask(&clip); |
614 gfx::Rect button_bounds(GetTabCloseButtonBounds(tab_bounds)); | 595 gfx::Point origin; |
615 gfx::Rect intersection(gfx::IntersectRects(tab_bounds, button_bounds)); | 596 ConvertPointToTarget(tab_, this, &origin); |
616 | 597 clip.offset(SkIntToScalar(origin.x()), SkIntToScalar(origin.y())); |
617 if (!intersection.IsEmpty()) { | 598 Op(*mask, clip, kIntersect_SkPathOp, mask); |
618 mask->addRect(RectToSkRect(intersection)); | 599 return !mask->isEmpty(); |
619 return true; | |
620 } | |
621 | |
622 return false; | |
623 } | |
624 | |
625 bool DoesIntersectRect(const View* target, | |
626 const gfx::Rect& rect) const override { | |
627 CHECK_EQ(target, this); | |
628 | |
629 // If the request is not made in response to a gesture, use the | |
630 // default implementation. | |
631 if (views::UsePointBasedTargeting(rect)) | |
632 return MaskedTargeterDelegate::DoesIntersectRect(target, rect); | |
633 | |
634 // The hit test request is in response to a gesture. Return false if any | |
635 // part of the tab close button is hidden from the user. | |
636 // TODO(tdanderson): Consider always returning the intersection if the | |
637 // non-rectangular shape of the tab can be accounted for. | |
638 gfx::Rect tab_bounds(GetTabBounds()); | |
639 gfx::Rect button_bounds(GetTabCloseButtonBounds(tab_bounds)); | |
640 if (!tab_bounds.Contains(button_bounds)) | |
641 return false; | |
642 | |
643 return MaskedTargeterDelegate::DoesIntersectRect(target, rect); | |
644 } | 600 } |
645 | 601 |
646 Tab* tab_; | 602 Tab* tab_; |
647 | 603 |
648 DISALLOW_COPY_AND_ASSIGN(TabCloseButton); | 604 DISALLOW_COPY_AND_ASSIGN(TabCloseButton); |
649 }; | 605 }; |
650 | 606 |
651 //////////////////////////////////////////////////////////////////////////////// | 607 //////////////////////////////////////////////////////////////////////////////// |
652 // ThrobberView | 608 // ThrobberView |
653 // | 609 // |
(...skipping 374 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1028 // Tab, views::MaskedTargeterDelegate overrides: | 984 // Tab, views::MaskedTargeterDelegate overrides: |
1029 | 985 |
1030 bool Tab::GetHitTestMask(gfx::Path* mask) const { | 986 bool Tab::GetHitTestMask(gfx::Path* mask) const { |
1031 const float scale = GetWidget()->GetCompositor()->device_scale_factor(); | 987 const float scale = GetWidget()->GetCompositor()->device_scale_factor(); |
1032 // When the window is maximized we don't want to shave off the edges or top | 988 // When the window is maximized we don't want to shave off the edges or top |
1033 // shadow of the tab, such that the user can click anywhere along the top | 989 // shadow of the tab, such that the user can click anywhere along the top |
1034 // edge of the screen to select a tab. Ditto for immersive fullscreen. | 990 // edge of the screen to select a tab. Ditto for immersive fullscreen. |
1035 const views::Widget* widget = GetWidget(); | 991 const views::Widget* widget = GetWidget(); |
1036 const bool extend_to_top = | 992 const bool extend_to_top = |
1037 widget && (widget->IsMaximized() || widget->IsFullscreen()); | 993 widget && (widget->IsMaximized() || widget->IsFullscreen()); |
1038 *mask = GetBorderPath(scale, true, extend_to_top, size()); | 994 auto border_getter = base::Bind(&GetBorderPath, scale, true, extend_to_top); |
sky
2016/08/04 23:42:08
tricky
| |
995 *mask = border_getter.Run(size()); | |
1039 | 996 |
1040 // It is possible for a portion of the tab to be occluded if tabs are | 997 // Clip the hittest mask if necessary for stacked tabs. This is primarily so |
1041 // stacked, so modify the hit test mask to only include the visible | 998 // we can use the clipped mask to clip the close button mask. |
1042 // region of the tab. | 999 // TODO(pkasting): Would it improve event targeting to do this clipping when |
1043 gfx::Rect clip; | 1000 // not stacking tabs as well? |
1044 controller_->ShouldPaintTab(this, &clip); | 1001 gfx::Path clip; |
1045 if (clip.size().GetArea()) { | 1002 controller_->ShouldPaintTab(this, border_getter, &clip); |
1046 SkRect intersection(mask->getBounds()); | 1003 Op(*mask, clip, kDifference_SkPathOp, mask); |
1047 mask->reset(); | 1004 return !mask->isEmpty(); |
1048 if (!intersection.intersect(RectToSkRect(clip))) | |
1049 return false; | |
1050 mask->addRect(intersection); | |
1051 } | |
1052 return true; | |
1053 } | 1005 } |
1054 | 1006 |
1055 //////////////////////////////////////////////////////////////////////////////// | 1007 //////////////////////////////////////////////////////////////////////////////// |
1056 // Tab, views::View overrides: | 1008 // Tab, views::View overrides: |
1057 | 1009 |
1058 void Tab::ViewHierarchyChanged(const ViewHierarchyChangedDetails& details) { | 1010 void Tab::ViewHierarchyChanged(const ViewHierarchyChangedDetails& details) { |
1059 // If this hierarchy changed has resulted in us being part of a widget | 1011 // If this hierarchy changed has resulted in us being part of a widget |
1060 // hierarchy for the first time, we can now get at the theme provider, and | 1012 // hierarchy for the first time, we can now get at the theme provider, and |
1061 // should recalculate the button color. | 1013 // should recalculate the button color. |
1062 if (details.is_add) | 1014 if (details.is_add) |
1063 OnButtonColorMaybeChanged(); | 1015 OnButtonColorMaybeChanged(); |
1064 } | 1016 } |
1065 | 1017 |
1066 void Tab::OnPaint(gfx::Canvas* canvas) { | 1018 void Tab::OnPaint(gfx::Canvas* canvas) { |
1067 // Don't paint if we're narrower than we can render correctly. (This should | 1019 // Don't paint if we're narrower than we can render correctly. (This should |
1068 // only happen during animations). | 1020 // only happen during animations). |
1069 if (width() < GetMinimumInactiveSize().width() && !data().pinned) | 1021 if (width() < GetMinimumInactiveSize().width() && !data().pinned) |
1070 return; | 1022 return; |
1071 | 1023 |
1072 gfx::Rect clip; | 1024 gfx::Path clip; |
1073 if (!controller_->ShouldPaintTab(this, &clip)) | 1025 if (!controller_->ShouldPaintTab( |
1026 this, base::Bind(&GetBorderPath, canvas->image_scale(), true, false), | |
1027 &clip)) | |
1074 return; | 1028 return; |
1075 if (!clip.IsEmpty()) { | |
1076 canvas->Save(); | |
1077 canvas->ClipRect(clip); | |
1078 } | |
1079 | 1029 |
1080 if (controller_->IsImmersiveStyle()) | 1030 if (controller_->IsImmersiveStyle()) |
1081 PaintImmersiveTab(canvas); | 1031 PaintImmersiveTab(canvas); |
1082 else | 1032 else |
1083 PaintTab(canvas); | 1033 PaintTab(canvas, clip); |
1084 | |
1085 if (!clip.IsEmpty()) | |
1086 canvas->Restore(); | |
1087 } | 1034 } |
1088 | 1035 |
1089 void Tab::Layout() { | 1036 void Tab::Layout() { |
1090 const gfx::Rect lb = GetContentsBounds(); | 1037 const gfx::Rect lb = GetContentsBounds(); |
1091 showing_icon_ = ShouldShowIcon(); | 1038 showing_icon_ = ShouldShowIcon(); |
1092 // See comments in IconCapacity(). | 1039 // See comments in IconCapacity(). |
1093 const int extra_padding = | 1040 const int extra_padding = |
1094 (controller_->ShouldHideCloseButtonForInactiveTabs() || | 1041 (controller_->ShouldHideCloseButtonForInactiveTabs() || |
1095 (IconCapacity() < 3)) ? 0 : kExtraLeftPaddingToBalanceCloseButtonPadding; | 1042 (IconCapacity() < 3)) ? 0 : kExtraLeftPaddingToBalanceCloseButtonPadding; |
1096 const int start = lb.x() + extra_padding; | 1043 const int start = lb.x() + extra_padding; |
(...skipping 266 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1363 | 1310 |
1364 if (data().blocked == old.blocked) | 1311 if (data().blocked == old.blocked) |
1365 return; | 1312 return; |
1366 | 1313 |
1367 if (data().blocked) | 1314 if (data().blocked) |
1368 StartPulse(); | 1315 StartPulse(); |
1369 else | 1316 else |
1370 StopPulse(); | 1317 StopPulse(); |
1371 } | 1318 } |
1372 | 1319 |
1373 void Tab::PaintTab(gfx::Canvas* canvas) { | 1320 void Tab::PaintTab(gfx::Canvas* canvas, const gfx::Path& clip) { |
1374 const int kActiveTabFillId = IDR_THEME_TOOLBAR; | 1321 const int kActiveTabFillId = IDR_THEME_TOOLBAR; |
1375 const bool has_custom_image = | 1322 const bool has_custom_image = |
1376 GetThemeProvider()->HasCustomImage(kActiveTabFillId); | 1323 GetThemeProvider()->HasCustomImage(kActiveTabFillId); |
1377 const int y_offset = -GetYInsetForActiveTabBackground(); | 1324 const int y_offset = -GetYInsetForActiveTabBackground(); |
1378 if (IsActive()) { | 1325 if (IsActive()) { |
1379 PaintTabBackgroundUsingFillId(canvas, true, kActiveTabFillId, | 1326 PaintTabBackgroundUsingFillId(canvas, canvas, true, kActiveTabFillId, |
1380 has_custom_image, y_offset); | 1327 has_custom_image, y_offset); |
1381 } else { | 1328 } else { |
1382 PaintInactiveTabBackground(canvas); | 1329 PaintInactiveTabBackground(canvas, clip); |
1383 | 1330 |
1384 const double throb_value = GetThrobValue(); | 1331 const double throb_value = GetThrobValue(); |
1385 if (throb_value > 0) { | 1332 if (throb_value > 0) { |
1386 canvas->SaveLayerAlpha(gfx::ToRoundedInt(throb_value * 0xff), | 1333 canvas->SaveLayerAlpha(gfx::ToRoundedInt(throb_value * 0xff), |
1387 GetLocalBounds()); | 1334 GetLocalBounds()); |
1388 PaintTabBackgroundUsingFillId(canvas, true, kActiveTabFillId, | 1335 PaintTabBackgroundUsingFillId(canvas, canvas, true, kActiveTabFillId, |
1389 has_custom_image, y_offset); | 1336 has_custom_image, y_offset); |
1390 canvas->Restore(); | 1337 canvas->Restore(); |
1391 } | 1338 } |
1392 } | 1339 } |
1393 | 1340 |
1394 if (showing_icon_) | 1341 if (showing_icon_) |
1395 PaintIcon(canvas); | 1342 PaintIcon(canvas); |
1396 } | 1343 } |
1397 | 1344 |
1398 void Tab::PaintImmersiveTab(gfx::Canvas* canvas) { | 1345 void Tab::PaintImmersiveTab(gfx::Canvas* canvas) { |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1432 canvas->FillRect(right_eye_rect, kEyeColor); | 1379 canvas->FillRect(right_eye_rect, kEyeColor); |
1433 // The second part parts the remaining |eye_width| on the left. | 1380 // The second part parts the remaining |eye_width| on the left. |
1434 int left_eye_width = eye_offset + eye_width - bar_rect.width(); | 1381 int left_eye_width = eye_offset + eye_width - bar_rect.width(); |
1435 gfx::Rect left_eye_rect( | 1382 gfx::Rect left_eye_rect( |
1436 bar_rect.x(), 0, left_eye_width, kImmersiveBarHeight); | 1383 bar_rect.x(), 0, left_eye_width, kImmersiveBarHeight); |
1437 canvas->FillRect(left_eye_rect, kEyeColor); | 1384 canvas->FillRect(left_eye_rect, kEyeColor); |
1438 } | 1385 } |
1439 } | 1386 } |
1440 } | 1387 } |
1441 | 1388 |
1442 void Tab::PaintInactiveTabBackground(gfx::Canvas* canvas) { | 1389 void Tab::PaintInactiveTabBackground(gfx::Canvas* canvas, |
1390 const gfx::Path& clip) { | |
1443 bool has_custom_image; | 1391 bool has_custom_image; |
1444 int fill_id = controller_->GetBackgroundResourceId(&has_custom_image); | 1392 int fill_id = controller_->GetBackgroundResourceId(&has_custom_image); |
1445 | 1393 |
1446 // If the theme is providing a custom background image, then its top edge | 1394 // If the theme is providing a custom background image, then its top edge |
1447 // should be at the top of the tab. Otherwise, we assume that the background | 1395 // should be at the top of the tab. Otherwise, we assume that the background |
1448 // image is a composited foreground + frame image. Note that if the theme is | 1396 // image is a composited foreground + frame image. Note that if the theme is |
1449 // only providing a custom frame image, |has_custom_image| will be true, but | 1397 // only providing a custom frame image, |has_custom_image| will be true, but |
1450 // we should use the |background_offset_| here. | 1398 // we should use the |background_offset_| here. |
1451 const ui::ThemeProvider* tp = GetThemeProvider(); | 1399 const ui::ThemeProvider* tp = GetThemeProvider(); |
1452 const int y_offset = tp->HasCustomImage(fill_id) ? | 1400 const int y_offset = tp->HasCustomImage(fill_id) ? |
1453 -GetLayoutConstant(TAB_TOP_EXCLUSION_HEIGHT) : background_offset_.y(); | 1401 -GetLayoutConstant(TAB_TOP_EXCLUSION_HEIGHT) : background_offset_.y(); |
1454 | 1402 |
1455 // We only cache the image when it's the default image and we're not hovered, | 1403 // We only cache the image when it's the default image and we're not hovered, |
1456 // to avoid caching a background image that isn't the same for all tabs. | 1404 // to avoid caching a background image that isn't the same for all tabs. |
1457 if (has_custom_image || hover_controller_.ShouldDraw()) { | 1405 if (has_custom_image || hover_controller_.ShouldDraw()) { |
1458 PaintTabBackgroundUsingFillId(canvas, false, fill_id, has_custom_image, | 1406 PaintTabBackgroundUsingFillId(canvas, canvas, false, fill_id, |
1459 y_offset); | 1407 has_custom_image, y_offset); |
1460 return; | 1408 return; |
1461 } | 1409 } |
1462 | 1410 |
1411 // For efficiency, we don't use separate fill and stroke images unless we | |
1412 // really need to clip the stroke and not the fill (for stacked tabs). This | |
1413 // saves memory and avoids an extra image draw at the cost of recalculating | |
1414 // the images when MaySetClip() toggles. | |
1415 const bool use_fill_and_stroke_images = | |
1416 controller_->MaySetClip() && | |
1417 ui::MaterialDesignController::IsModeMaterial(); | |
1418 | |
1463 const ImageCacheEntryMetadata metadata( | 1419 const ImageCacheEntryMetadata metadata( |
1464 fill_id, tp->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB), | 1420 fill_id, tp->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB), |
1465 controller_->GetToolbarTopSeparatorColor(), | 1421 controller_->GetToolbarTopSeparatorColor(), use_fill_and_stroke_images, |
1466 ui::GetSupportedScaleFactor(canvas->image_scale()), size()); | 1422 ui::GetSupportedScaleFactor(canvas->image_scale()), size()); |
1467 auto it = std::find_if( | 1423 auto it = std::find_if( |
1468 g_image_cache->begin(), g_image_cache->end(), | 1424 g_image_cache->begin(), g_image_cache->end(), |
1469 [&metadata](const ImageCacheEntry& e) { return e.metadata == metadata; }); | 1425 [&metadata](const ImageCacheEntry& e) { return e.metadata == metadata; }); |
1470 if (it == g_image_cache->end()) { | 1426 if (it == g_image_cache->end()) { |
1471 gfx::Canvas tmp_canvas(size(), canvas->image_scale(), false); | 1427 gfx::Canvas tmp_canvas(size(), canvas->image_scale(), false); |
1472 PaintTabBackgroundUsingFillId(&tmp_canvas, false, fill_id, false, y_offset); | 1428 if (use_fill_and_stroke_images) { |
1473 g_image_cache->emplace_front(metadata, | 1429 gfx::Canvas tmp_fill_canvas(size(), canvas->image_scale(), false); |
1474 gfx::ImageSkia(tmp_canvas.ExtractImageRep())); | 1430 PaintTabBackgroundUsingFillId(&tmp_fill_canvas, &tmp_canvas, false, |
1431 fill_id, false, y_offset); | |
1432 g_image_cache->emplace_front( | |
1433 metadata, gfx::ImageSkia(tmp_fill_canvas.ExtractImageRep()), | |
1434 gfx::ImageSkia(tmp_canvas.ExtractImageRep())); | |
1435 } else { | |
1436 PaintTabBackgroundUsingFillId(&tmp_canvas, &tmp_canvas, false, fill_id, | |
1437 false, y_offset); | |
1438 g_image_cache->emplace_front( | |
1439 metadata, gfx::ImageSkia(), | |
1440 gfx::ImageSkia(tmp_canvas.ExtractImageRep())); | |
1441 } | |
1475 if (g_image_cache->size() > kMaxImageCacheSize) | 1442 if (g_image_cache->size() > kMaxImageCacheSize) |
1476 g_image_cache->pop_back(); | 1443 g_image_cache->pop_back(); |
1477 it = g_image_cache->begin(); | 1444 it = g_image_cache->begin(); |
1478 } | 1445 } |
1479 canvas->DrawImageInt(it->image, 0, 0); | 1446 |
1447 gfx::ScopedCanvas scoped_canvas( | |
1448 use_fill_and_stroke_images ? canvas : nullptr); | |
1449 if (use_fill_and_stroke_images) { | |
1450 canvas->DrawImageInt(it->fill_image, 0, 0); | |
1451 canvas->sk_canvas()->clipPath(clip, SkRegion::kDifference_Op, true); | |
1452 } | |
1453 canvas->DrawImageInt(it->stroke_image, 0, 0); | |
1480 } | 1454 } |
1481 | 1455 |
1482 void Tab::PaintTabBackgroundUsingFillId(gfx::Canvas* canvas, | 1456 void Tab::PaintTabBackgroundUsingFillId(gfx::Canvas* fill_canvas, |
1457 gfx::Canvas* stroke_canvas, | |
1483 bool is_active, | 1458 bool is_active, |
1484 int fill_id, | 1459 int fill_id, |
1485 bool has_custom_image, | 1460 bool has_custom_image, |
1486 int y_offset) { | 1461 int y_offset) { |
1487 const ui::ThemeProvider* tp = GetThemeProvider(); | 1462 const ui::ThemeProvider* tp = GetThemeProvider(); |
1488 const SkColor toolbar_color = tp->GetColor(ThemeProperties::COLOR_TOOLBAR); | 1463 const SkColor toolbar_color = tp->GetColor(ThemeProperties::COLOR_TOOLBAR); |
1489 gfx::ImageSkia* fill_image = tp->GetImageSkiaNamed(fill_id); | 1464 gfx::ImageSkia* fill_image = tp->GetImageSkiaNamed(fill_id); |
1490 // The tab image needs to be lined up with the background image | 1465 // The tab image needs to be lined up with the background image |
1491 // so that it feels partially transparent. These offsets represent the tab | 1466 // so that it feels partially transparent. These offsets represent the tab |
1492 // position within the frame background image. | 1467 // position within the frame background image. |
1493 const int x_offset = GetMirroredX() + background_offset_.x(); | 1468 const int x_offset = GetMirroredX() + background_offset_.x(); |
1494 | 1469 |
1495 const SkScalar kMinHoverRadius = 16; | 1470 const SkScalar kMinHoverRadius = 16; |
1496 const SkScalar radius = | 1471 const SkScalar radius = |
1497 std::max(SkFloatToScalar(width() / 4.f), kMinHoverRadius); | 1472 std::max(SkFloatToScalar(width() / 4.f), kMinHoverRadius); |
1498 const bool draw_hover = !is_active && hover_controller_.ShouldDraw(); | 1473 const bool draw_hover = !is_active && hover_controller_.ShouldDraw(); |
1499 SkPoint hover_location(gfx::PointToSkPoint(hover_controller_.location())); | 1474 SkPoint hover_location(gfx::PointToSkPoint(hover_controller_.location())); |
1500 const SkColor hover_color = | 1475 const SkColor hover_color = |
1501 SkColorSetA(toolbar_color, hover_controller_.GetAlpha()); | 1476 SkColorSetA(toolbar_color, hover_controller_.GetAlpha()); |
1502 | 1477 |
1503 if (ui::MaterialDesignController::IsModeMaterial()) { | 1478 if (ui::MaterialDesignController::IsModeMaterial()) { |
1504 gfx::ScopedCanvas scoped_canvas(canvas); | 1479 gfx::Path fill; |
1505 const float scale = canvas->UndoDeviceScaleFactor(); | 1480 SkPaint paint; |
1481 paint.setAntiAlias(true); | |
1506 | 1482 |
1507 // Draw the fill. | 1483 // Draw the fill. |
1508 gfx::Path fill = GetFillPath(scale, size()); | |
1509 SkPaint paint; | |
1510 paint.setAntiAlias(true); | |
1511 { | 1484 { |
1512 gfx::ScopedCanvas clip_scoper(canvas); | 1485 gfx::ScopedCanvas scoped_fill_canvas(fill_canvas); |
1513 canvas->ClipPath(fill, true); | 1486 const float scale = fill_canvas->UndoDeviceScaleFactor(); |
1514 if (has_custom_image) { | 1487 |
1515 gfx::ScopedCanvas scale_scoper(canvas); | 1488 fill = GetFillPath(scale, size()); |
1516 canvas->sk_canvas()->scale(scale, scale); | 1489 { |
1517 canvas->TileImageInt(*fill_image, x_offset, y_offset, 0, 0, width(), | 1490 gfx::ScopedCanvas clip_scoper(fill_canvas); |
1518 height()); | 1491 fill_canvas->ClipPath(fill, true); |
1519 } else { | 1492 if (has_custom_image) { |
1520 paint.setColor( | 1493 gfx::ScopedCanvas scale_scoper(fill_canvas); |
1521 is_active ? toolbar_color | 1494 fill_canvas->sk_canvas()->scale(scale, scale); |
1522 : tp->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB)); | 1495 fill_canvas->TileImageInt(*fill_image, x_offset, y_offset, 0, 0, |
1523 canvas->DrawRect(gfx::ScaleToEnclosingRect(GetLocalBounds(), scale), | 1496 width(), height()); |
1524 paint); | 1497 } else { |
1525 } | 1498 paint.setColor( |
1526 if (draw_hover) { | 1499 is_active ? toolbar_color |
1527 hover_location.scale(SkFloatToScalar(scale)); | 1500 : tp->GetColor(ThemeProperties::COLOR_BACKGROUND_TAB)); |
1528 DrawHighlight(canvas, hover_location, radius * scale, hover_color); | 1501 fill_canvas->DrawRect( |
1502 gfx::ScaleToEnclosingRect(GetLocalBounds(), scale), paint); | |
1503 } | |
1504 if (draw_hover) { | |
1505 hover_location.scale(SkFloatToScalar(scale)); | |
1506 DrawHighlight(fill_canvas, hover_location, radius * scale, | |
1507 hover_color); | |
1508 } | |
1529 } | 1509 } |
1530 } | 1510 } |
1531 | 1511 |
1532 // Draw the stroke. | 1512 // Draw the stroke. |
1533 gfx::Path stroke = GetBorderPath(scale, false, false, size()); | 1513 { |
1534 Op(stroke, fill, kDifference_SkPathOp, &stroke); | 1514 gfx::ScopedCanvas scoped_stroke_canvas(stroke_canvas); |
1535 if (!is_active) { | 1515 const float scale = stroke_canvas->UndoDeviceScaleFactor(); |
1536 // Clip out the bottom line; this will be drawn for us by | 1516 |
1537 // TabStrip::PaintChildren(). | 1517 gfx::Path stroke = GetBorderPath(scale, false, false, size()); |
1538 canvas->ClipRect(gfx::RectF(width() * scale, height() * scale - 1)); | 1518 Op(stroke, fill, kDifference_SkPathOp, &stroke); |
1519 if (!is_active) { | |
1520 // Clip out the bottom line; this will be drawn for us by | |
1521 // TabStrip::PaintChildren(). | |
1522 stroke_canvas->ClipRect(gfx::RectF(width() * scale, | |
1523 height() * scale - 1)); | |
1524 } | |
1525 paint.setColor(controller_->GetToolbarTopSeparatorColor()); | |
1526 stroke_canvas->DrawPath(stroke, paint); | |
1539 } | 1527 } |
1540 paint.setColor(controller_->GetToolbarTopSeparatorColor()); | |
1541 canvas->DrawPath(stroke, paint); | |
1542 } else { | 1528 } else { |
1529 gfx::Canvas* canvas = stroke_canvas; | |
1543 if (draw_hover) { | 1530 if (draw_hover) { |
1544 // Draw everything to a temporary canvas so we can extract an image for | 1531 // Draw everything to a temporary canvas so we can extract an image for |
1545 // use in masking the hover glow. | 1532 // use in masking the hover glow. |
1546 gfx::Canvas background_canvas(size(), canvas->image_scale(), false); | 1533 gfx::Canvas background_canvas(size(), canvas->image_scale(), false); |
1547 PaintTabFill(&background_canvas, fill_image, x_offset, y_offset, size(), | 1534 PaintTabFill(&background_canvas, fill_image, x_offset, y_offset, size(), |
1548 is_active); | 1535 is_active); |
1549 gfx::ImageSkia background_image(background_canvas.ExtractImageRep()); | 1536 gfx::ImageSkia background_image(background_canvas.ExtractImageRep()); |
1550 canvas->DrawImageInt(background_image, 0, 0); | 1537 canvas->DrawImageInt(background_image, 0, 0); |
1551 | 1538 |
1552 gfx::Canvas hover_canvas(size(), canvas->image_scale(), false); | 1539 gfx::Canvas hover_canvas(size(), canvas->image_scale(), false); |
(...skipping 256 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1809 gfx::Rect Tab::GetImmersiveBarRect() const { | 1796 gfx::Rect Tab::GetImmersiveBarRect() const { |
1810 // The main bar is as wide as the normal tab's horizontal top line. | 1797 // The main bar is as wide as the normal tab's horizontal top line. |
1811 // This top line of the tab extends a few pixels left and right of the | 1798 // This top line of the tab extends a few pixels left and right of the |
1812 // center image due to pixels in the rounded corner images. | 1799 // center image due to pixels in the rounded corner images. |
1813 const int kBarPadding = 1; | 1800 const int kBarPadding = 1; |
1814 int main_bar_left = g_active_images.l_width - kBarPadding; | 1801 int main_bar_left = g_active_images.l_width - kBarPadding; |
1815 int main_bar_right = width() - g_active_images.r_width + kBarPadding; | 1802 int main_bar_right = width() - g_active_images.r_width + kBarPadding; |
1816 return gfx::Rect( | 1803 return gfx::Rect( |
1817 main_bar_left, 0, main_bar_right - main_bar_left, kImmersiveBarHeight); | 1804 main_bar_left, 0, main_bar_right - main_bar_left, kImmersiveBarHeight); |
1818 } | 1805 } |
OLD | NEW |