| 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 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 90 const double kSelectedTabOpacity = 0.3; | 90 const double kSelectedTabOpacity = 0.3; |
| 91 | 91 |
| 92 // Inactive selected tabs have their throb value scaled by this. | 92 // Inactive selected tabs have their throb value scaled by this. |
| 93 const double kSelectedTabThrobScale = 0.95 - kSelectedTabOpacity; | 93 const double kSelectedTabThrobScale = 0.95 - kSelectedTabOpacity; |
| 94 | 94 |
| 95 // Max number of images to cache. This has to be at least two since rounding | 95 // Max number of images to cache. This has to be at least two since rounding |
| 96 // errors may lead to tabs in the same tabstrip having different sizes. | 96 // errors may lead to tabs in the same tabstrip having different sizes. |
| 97 // 8 = normal/incognito, active/inactive, 2 sizes within tabstrip. | 97 // 8 = normal/incognito, active/inactive, 2 sizes within tabstrip. |
| 98 const size_t kMaxImageCacheSize = 8; | 98 const size_t kMaxImageCacheSize = 8; |
| 99 | 99 |
| 100 // Height of the miniature tab strip in immersive mode. | |
| 101 const int kImmersiveTabHeight = 3; | |
| 102 | |
| 103 // Height of the small tab indicator rectangles in immersive mode. | |
| 104 const int kImmersiveBarHeight = 2; | |
| 105 | |
| 106 // Color for active and inactive tabs in the immersive mode light strip. These | |
| 107 // should be a little brighter than the color of the normal art assets for tabs, | |
| 108 // which for active tabs is 230, 230, 230 and for inactive is 184, 184, 184. | |
| 109 const SkColor kImmersiveActiveTabColor = SkColorSetRGB(235, 235, 235); | |
| 110 const SkColor kImmersiveInactiveTabColor = SkColorSetRGB(190, 190, 190); | |
| 111 | |
| 112 // The minimum opacity (out of 1) when a tab (either active or inactive) is | |
| 113 // throbbing in the immersive mode light strip. | |
| 114 const double kImmersiveTabMinThrobOpacity = 0.66; | |
| 115 | |
| 116 // Number of steps in the immersive mode loading animation. | |
| 117 const int kImmersiveLoadingStepCount = 32; | |
| 118 | |
| 119 const char kTabCloseButtonName[] = "TabCloseButton"; | 100 const char kTabCloseButtonName[] = "TabCloseButton"; |
| 120 | 101 |
| 121 //////////////////////////////////////////////////////////////////////////////// | 102 //////////////////////////////////////////////////////////////////////////////// |
| 122 // ImageCacheEntryMetadata | 103 // ImageCacheEntryMetadata |
| 123 // | 104 // |
| 124 // All metadata necessary to uniquely identify a cached image. | 105 // All metadata necessary to uniquely identify a cached image. |
| 125 struct ImageCacheEntryMetadata { | 106 struct ImageCacheEntryMetadata { |
| 126 ImageCacheEntryMetadata(SkColor fill_color, | 107 ImageCacheEntryMetadata(SkColor fill_color, |
| 127 SkColor stroke_color, | 108 SkColor stroke_color, |
| 128 bool use_fill_and_stroke_images, | 109 bool use_fill_and_stroke_images, |
| (...skipping 401 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 530 | 511 |
| 531 // static | 512 // static |
| 532 const char Tab::kViewClassName[] = "Tab"; | 513 const char Tab::kViewClassName[] = "Tab"; |
| 533 | 514 |
| 534 Tab::Tab(TabController* controller, gfx::AnimationContainer* container) | 515 Tab::Tab(TabController* controller, gfx::AnimationContainer* container) |
| 535 : controller_(controller), | 516 : controller_(controller), |
| 536 closing_(false), | 517 closing_(false), |
| 537 dragging_(false), | 518 dragging_(false), |
| 538 detached_(false), | 519 detached_(false), |
| 539 favicon_hiding_offset_(0), | 520 favicon_hiding_offset_(0), |
| 540 immersive_loading_step_(0), | |
| 541 should_display_crashed_favicon_(false), | 521 should_display_crashed_favicon_(false), |
| 542 pulse_animation_(new gfx::ThrobAnimation(this)), | 522 pulse_animation_(new gfx::ThrobAnimation(this)), |
| 543 crash_icon_animation_(new FaviconCrashAnimation(this)), | 523 crash_icon_animation_(new FaviconCrashAnimation(this)), |
| 544 animation_container_(container), | 524 animation_container_(container), |
| 545 throbber_(nullptr), | 525 throbber_(nullptr), |
| 546 alert_indicator_button_(nullptr), | 526 alert_indicator_button_(nullptr), |
| 547 close_button_(nullptr), | 527 close_button_(nullptr), |
| 548 title_(new views::Label()), | 528 title_(new views::Label()), |
| 549 tab_activated_with_last_tap_down_(false), | 529 tab_activated_with_last_tap_down_(false), |
| 550 hover_controller_(this), | 530 hover_controller_(this), |
| (...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 739 return kTouchWidth; | 719 return kTouchWidth; |
| 740 } | 720 } |
| 741 | 721 |
| 742 // static | 722 // static |
| 743 int Tab::GetPinnedWidth() { | 723 int Tab::GetPinnedWidth() { |
| 744 constexpr int kTabPinnedContentWidth = 23; | 724 constexpr int kTabPinnedContentWidth = 23; |
| 745 return GetMinimumInactiveSize().width() + kTabPinnedContentWidth; | 725 return GetMinimumInactiveSize().width() + kTabPinnedContentWidth; |
| 746 } | 726 } |
| 747 | 727 |
| 748 // static | 728 // static |
| 749 int Tab::GetImmersiveHeight() { | |
| 750 return kImmersiveTabHeight; | |
| 751 } | |
| 752 | |
| 753 // static | |
| 754 float Tab::GetInverseDiagonalSlope() { | 729 float Tab::GetInverseDiagonalSlope() { |
| 755 // This is computed from the border path as follows: | 730 // This is computed from the border path as follows: |
| 756 // * The unscaled endcap width is enough for the whole stroke outer curve, | 731 // * The unscaled endcap width is enough for the whole stroke outer curve, |
| 757 // i.e. the side diagonal plus the curves on both its ends. | 732 // i.e. the side diagonal plus the curves on both its ends. |
| 758 // * The bottom and top curve are each (2 * scale) px wide, so the diagonal is | 733 // * The bottom and top curve are each (2 * scale) px wide, so the diagonal is |
| 759 // (unscaled endcap width - 2 - 2) * scale px wide. | 734 // (unscaled endcap width - 2 - 2) * scale px wide. |
| 760 // * The bottom and top curve are each 1.5 px high. Additionally, there is an | 735 // * The bottom and top curve are each 1.5 px high. Additionally, there is an |
| 761 // extra 1 px below the bottom curve and (scale - 1) px above the top curve, | 736 // extra 1 px below the bottom curve and (scale - 1) px above the top curve, |
| 762 // so the diagonal is ((height - 1.5 - 1.5) * scale - 1 - (scale - 1)) px | 737 // so the diagonal is ((height - 1.5 - 1.5) * scale - 1 - (scale - 1)) px |
| 763 // high. | 738 // high. |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 847 // only happen during animations). | 822 // only happen during animations). |
| 848 if (width() < GetMinimumInactiveSize().width() && !data().pinned) | 823 if (width() < GetMinimumInactiveSize().width() && !data().pinned) |
| 849 return; | 824 return; |
| 850 | 825 |
| 851 gfx::Path clip; | 826 gfx::Path clip; |
| 852 if (!controller_->ShouldPaintTab( | 827 if (!controller_->ShouldPaintTab( |
| 853 this, base::Bind(&GetBorderPath, canvas->image_scale(), true, false), | 828 this, base::Bind(&GetBorderPath, canvas->image_scale(), true, false), |
| 854 &clip)) | 829 &clip)) |
| 855 return; | 830 return; |
| 856 | 831 |
| 857 if (controller_->IsImmersiveStyle()) | 832 PaintTab(canvas, clip); |
| 858 PaintImmersiveTab(canvas); | |
| 859 else | |
| 860 PaintTab(canvas, clip); | |
| 861 } | 833 } |
| 862 | 834 |
| 863 void Tab::Layout() { | 835 void Tab::Layout() { |
| 864 const gfx::Rect lb = GetContentsBounds(); | 836 const gfx::Rect lb = GetContentsBounds(); |
| 865 showing_icon_ = ShouldShowIcon(); | 837 showing_icon_ = ShouldShowIcon(); |
| 866 // See comments in IconCapacity(). | 838 // See comments in IconCapacity(). |
| 867 const int extra_padding = | 839 const int extra_padding = |
| 868 (controller_->ShouldHideCloseButtonForInactiveTabs() || | 840 (controller_->ShouldHideCloseButtonForInactiveTabs() || |
| 869 (IconCapacity() < 3)) ? 0 : kExtraLeftPaddingToBalanceCloseButtonPadding; | 841 (IconCapacity() < 3)) ? 0 : kExtraLeftPaddingToBalanceCloseButtonPadding; |
| 870 const int start = lb.x() + extra_padding; | 842 const int start = lb.x() + extra_padding; |
| (...skipping 291 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1162 PaintTabBackgroundUsingFillId(canvas, canvas, true, kActiveTabFillId, | 1134 PaintTabBackgroundUsingFillId(canvas, canvas, true, kActiveTabFillId, |
| 1163 y_offset); | 1135 y_offset); |
| 1164 canvas->Restore(); | 1136 canvas->Restore(); |
| 1165 } | 1137 } |
| 1166 } | 1138 } |
| 1167 | 1139 |
| 1168 if (showing_icon_) | 1140 if (showing_icon_) |
| 1169 PaintIcon(canvas); | 1141 PaintIcon(canvas); |
| 1170 } | 1142 } |
| 1171 | 1143 |
| 1172 void Tab::PaintImmersiveTab(gfx::Canvas* canvas) { | |
| 1173 // Use transparency for the draw-attention animation. | |
| 1174 int alpha = 255; | |
| 1175 if (pulse_animation_->is_animating() && !data().pinned) { | |
| 1176 alpha = pulse_animation_->CurrentValueBetween( | |
| 1177 255, gfx::ToRoundedInt(255 * kImmersiveTabMinThrobOpacity)); | |
| 1178 } | |
| 1179 | |
| 1180 // Draw a gray rectangle to represent the tab. This works for pinned tabs as | |
| 1181 // well as regular ones. The active tab has a brigher bar. | |
| 1182 SkColor color = | |
| 1183 IsActive() ? kImmersiveActiveTabColor : kImmersiveInactiveTabColor; | |
| 1184 gfx::Rect bar_rect = GetImmersiveBarRect(); | |
| 1185 canvas->FillRect(bar_rect, SkColorSetA(color, alpha)); | |
| 1186 | |
| 1187 // Paint network activity indicator. | |
| 1188 // TODO(jamescook): Replace this placeholder animation with a real one. | |
| 1189 // For now, let's go with a Cylon eye effect, but in blue. | |
| 1190 if (data().network_state != TabRendererData::NETWORK_STATE_NONE && | |
| 1191 data().network_state != TabRendererData::NETWORK_STATE_ERROR) { | |
| 1192 const SkColor kEyeColor = SkColorSetARGB(alpha, 71, 138, 217); | |
| 1193 int eye_width = bar_rect.width() / 3; | |
| 1194 int eye_offset = bar_rect.width() * immersive_loading_step_ / | |
| 1195 kImmersiveLoadingStepCount; | |
| 1196 if (eye_offset + eye_width < bar_rect.width()) { | |
| 1197 // Draw a single indicator strip because it fits inside |bar_rect|. | |
| 1198 gfx::Rect eye_rect( | |
| 1199 bar_rect.x() + eye_offset, 0, eye_width, kImmersiveBarHeight); | |
| 1200 canvas->FillRect(eye_rect, kEyeColor); | |
| 1201 } else { | |
| 1202 // Draw two indicators to simulate the eye "wrapping around" to the left | |
| 1203 // side. The first part fills the remainder of the bar. | |
| 1204 int right_eye_width = bar_rect.width() - eye_offset; | |
| 1205 gfx::Rect right_eye_rect( | |
| 1206 bar_rect.x() + eye_offset, 0, right_eye_width, kImmersiveBarHeight); | |
| 1207 canvas->FillRect(right_eye_rect, kEyeColor); | |
| 1208 // The second part parts the remaining |eye_width| on the left. | |
| 1209 int left_eye_width = eye_offset + eye_width - bar_rect.width(); | |
| 1210 gfx::Rect left_eye_rect( | |
| 1211 bar_rect.x(), 0, left_eye_width, kImmersiveBarHeight); | |
| 1212 canvas->FillRect(left_eye_rect, kEyeColor); | |
| 1213 } | |
| 1214 } | |
| 1215 } | |
| 1216 | |
| 1217 void Tab::PaintInactiveTabBackground(gfx::Canvas* canvas, | 1144 void Tab::PaintInactiveTabBackground(gfx::Canvas* canvas, |
| 1218 const gfx::Path& clip) { | 1145 const gfx::Path& clip) { |
| 1219 bool has_custom_image; | 1146 bool has_custom_image; |
| 1220 int fill_id = controller_->GetBackgroundResourceId(&has_custom_image); | 1147 int fill_id = controller_->GetBackgroundResourceId(&has_custom_image); |
| 1221 const ui::ThemeProvider* tp = GetThemeProvider(); | 1148 const ui::ThemeProvider* tp = GetThemeProvider(); |
| 1222 | 1149 |
| 1223 // We only cache the image when it's the default image and we're not hovered, | 1150 // We only cache the image when it's the default image and we're not hovered, |
| 1224 // to avoid caching a background image that isn't the same for all tabs. | 1151 // to avoid caching a background image that isn't the same for all tabs. |
| 1225 if (has_custom_image) { | 1152 if (has_custom_image) { |
| 1226 // If the theme is providing a custom background image, then its top edge | 1153 // If the theme is providing a custom background image, then its top edge |
| (...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1421 PaintPinnedTabTitleChangedIndicatorAndIcon(canvas, bounds); | 1348 PaintPinnedTabTitleChangedIndicatorAndIcon(canvas, bounds); |
| 1422 } else if (!favicon_.isNull()) { | 1349 } else if (!favicon_.isNull()) { |
| 1423 canvas->DrawImageInt(favicon_, 0, 0, bounds.width(), bounds.height(), | 1350 canvas->DrawImageInt(favicon_, 0, 0, bounds.width(), bounds.height(), |
| 1424 bounds.x(), bounds.y(), bounds.width(), | 1351 bounds.x(), bounds.y(), bounds.width(), |
| 1425 bounds.height(), false); | 1352 bounds.height(), false); |
| 1426 } | 1353 } |
| 1427 } | 1354 } |
| 1428 | 1355 |
| 1429 void Tab::AdvanceLoadingAnimation() { | 1356 void Tab::AdvanceLoadingAnimation() { |
| 1430 const TabRendererData::NetworkState state = data().network_state; | 1357 const TabRendererData::NetworkState state = data().network_state; |
| 1431 if (controller_->IsImmersiveStyle()) { | |
| 1432 throbber_->SetVisible(false); | |
| 1433 if (state == TabRendererData::NETWORK_STATE_WAITING) { | |
| 1434 // Waiting steps backwards. | |
| 1435 immersive_loading_step_ = | |
| 1436 (immersive_loading_step_ - 1 + kImmersiveLoadingStepCount) % | |
| 1437 kImmersiveLoadingStepCount; | |
| 1438 } else if (state == TabRendererData::NETWORK_STATE_LOADING) { | |
| 1439 immersive_loading_step_ = | |
| 1440 (immersive_loading_step_ + 1) % kImmersiveLoadingStepCount; | |
| 1441 } else { | |
| 1442 immersive_loading_step_ = 0; | |
| 1443 } | |
| 1444 | |
| 1445 SchedulePaintInRect(GetImmersiveBarRect()); | |
| 1446 return; | |
| 1447 } | |
| 1448 | |
| 1449 if (state == TabRendererData::NETWORK_STATE_NONE || | 1358 if (state == TabRendererData::NETWORK_STATE_NONE || |
| 1450 state == TabRendererData::NETWORK_STATE_ERROR) { | 1359 state == TabRendererData::NETWORK_STATE_ERROR) { |
| 1451 throbber_->ResetStartTimes(); | 1360 throbber_->ResetStartTimes(); |
| 1452 throbber_->SetVisible(false); | 1361 throbber_->SetVisible(false); |
| 1453 ScheduleIconPaint(); | 1362 ScheduleIconPaint(); |
| 1454 return; | 1363 return; |
| 1455 } | 1364 } |
| 1456 | 1365 |
| 1457 // Since the throbber can animate for a long time, paint to a separate layer | 1366 // Since the throbber can animate for a long time, paint to a separate layer |
| 1458 // when possible to reduce repaint overhead. | 1367 // when possible to reduce repaint overhead. |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1574 gfx::Rect bounds = favicon_bounds_; | 1483 gfx::Rect bounds = favicon_bounds_; |
| 1575 if (bounds.IsEmpty()) | 1484 if (bounds.IsEmpty()) |
| 1576 return; | 1485 return; |
| 1577 | 1486 |
| 1578 // Extends the area to the bottom when the crash animation is in progress. | 1487 // Extends the area to the bottom when the crash animation is in progress. |
| 1579 if (crash_icon_animation_->is_animating()) | 1488 if (crash_icon_animation_->is_animating()) |
| 1580 bounds.set_height(height() - bounds.y()); | 1489 bounds.set_height(height() - bounds.y()); |
| 1581 bounds.set_x(GetMirroredXForRect(bounds)); | 1490 bounds.set_x(GetMirroredXForRect(bounds)); |
| 1582 SchedulePaintInRect(bounds); | 1491 SchedulePaintInRect(bounds); |
| 1583 } | 1492 } |
| 1584 | |
| 1585 gfx::Rect Tab::GetImmersiveBarRect() const { | |
| 1586 // The main bar is as wide as the normal tab's horizontal top line. | |
| 1587 gfx::Rect contents = GetContentsBounds(); | |
| 1588 contents.set_y(0); | |
| 1589 contents.set_height(kImmersiveBarHeight); | |
| 1590 return contents; | |
| 1591 } | |
| OLD | NEW |