blob: 964f81c43b85c0990a373639755cb8aba63bd043 [file] [log] [blame]
// Copyright (c) 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.h"
#include <algorithm>
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/browser_features.h"
#include "chrome/browser/ui/views/chrome_typography.h"
#include "chrome/browser/ui/views/tabs/tab.h"
#include "chrome/browser/ui/views/tabs/tab_renderer_data.h"
#include "chrome/browser/ui/views/tabs/tab_style.h"
#include "components/url_formatter/url_formatter.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/layout_provider.h"
#include "ui/views/view_properties.h"
#include "ui/views/widget/widget.h"
namespace {
constexpr base::TimeDelta kMinimumTriggerDelay =
base::TimeDelta::FromMilliseconds(50);
constexpr base::TimeDelta kMaximumTriggerDelay =
base::TimeDelta::FromMilliseconds(1000);
// Hover card and preview image dimensions.
constexpr int kPreferredTabHoverCardWidth = 240;
constexpr float kTabHoverCardPreviewImageAspectRatio = 16.0f / 9.0f;
constexpr gfx::Size kTabHoverCardPreviewImageSize = gfx::Size(
kPreferredTabHoverCardWidth,
kPreferredTabHoverCardWidth / kTabHoverCardPreviewImageAspectRatio);
bool AreHoverCardImagesEnabled() {
return base::FeatureList::IsEnabled(features::kTabHoverCardImages);
}
} // namespace
TabHoverCardBubbleView::TabHoverCardBubbleView(Tab* tab)
: BubbleDialogDelegateView(tab, views::BubbleBorder::TOP_LEFT) {
SetLayoutManager(
std::make_unique<views::BoxLayout>(views::BoxLayout::kVertical));
// Set so that when hovering over a tab in a inactive window that window will
// not become active. Setting this to false creates the need to explicitly
// hide the hovercard on press, touch, and keyboard events.
SetCanActivate(false);
title_label_ =
new views::Label(base::string16(), CONTEXT_TAB_HOVER_CARD_TITLE,
views::style::STYLE_PRIMARY);
title_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
title_label_->SetMultiLine(false);
AddChildView(title_label_);
domain_label_ = new views::Label(base::string16(), CONTEXT_BODY_TEXT_LARGE,
ChromeTextStyle::STYLE_SECONDARY);
domain_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
domain_label_->SetMultiLine(false);
AddChildView(domain_label_);
if (AreHoverCardImagesEnabled()) {
preview_image_ = new views::ImageView();
preview_image_->SetVisible(AreHoverCardImagesEnabled());
preview_image_->SetHorizontalAlignment(views::ImageViewBase::LEADING);
constexpr gfx::Insets kPreviewImageMargins(12, 0, 0, 0);
preview_image_->SetProperty(views::kMarginsKey,
new gfx::Insets(kPreviewImageMargins));
AddChildView(preview_image_);
}
widget_ = views::BubbleDialogDelegateView::CreateBubble(this);
}
TabHoverCardBubbleView::~TabHoverCardBubbleView() = default;
void TabHoverCardBubbleView::UpdateAndShow(Tab* tab) {
UpdateCardContent(tab->data());
views::BubbleDialogDelegateView::SetAnchorView(tab);
// Start trigger timer if necessary.
if (widget_->IsVisible()) {
ShowImmediately();
} else {
// Note that this will restart the timer if it is already running. If the
// hover cards are not yet visible, moving the cursor within the tabstrip
// will not trigger the hover cards.
delayed_show_timer_.Start(FROM_HERE, GetDelay(tab->width()), this,
&TabHoverCardBubbleView::ShowImmediately);
}
}
void TabHoverCardBubbleView::Hide() {
delayed_show_timer_.Stop();
widget_->Hide();
}
int TabHoverCardBubbleView::GetDialogButtons() const {
return ui::DIALOG_BUTTON_NONE;
}
base::TimeDelta TabHoverCardBubbleView::GetDelay(int tab_width) const {
// Delay is calculated as a logarithmic scale and bounded by a minimum width
// based on the width of a pinned tab and a maximum of the standard width.
//
// delay (ms)
// |
// max delay-| *
// | *
// | *
// | *
// | *
// | *
// | *
// | *
// | *
// min delay-|****
// |___________________________________________ tab width
// | |
// pinned tab width standard tab width
if (tab_width < TabStyle::GetPinnedWidth())
return kMinimumTriggerDelay;
double logarithmic_fraction =
std::log(tab_width - TabStyle::GetPinnedWidth() + 1) /
std::log(TabStyle::GetStandardWidth() - TabStyle::GetPinnedWidth() + 1);
base::TimeDelta scaling_factor = kMaximumTriggerDelay - kMinimumTriggerDelay;
base::TimeDelta delay =
logarithmic_fraction * scaling_factor + kMinimumTriggerDelay;
return delay;
}
void TabHoverCardBubbleView::ShowImmediately() {
widget_->Show();
}
void TabHoverCardBubbleView::UpdateCardContent(TabRendererData data) {
title_label_->SetText(data.title);
base::string16 domain = url_formatter::FormatUrl(
data.url,
url_formatter::kFormatUrlOmitUsernamePassword |
url_formatter::kFormatUrlOmitHTTPS |
url_formatter::kFormatUrlOmitHTTP |
url_formatter::kFormatUrlOmitTrailingSlashOnBareHostname |
url_formatter::kFormatUrlOmitTrivialSubdomains |
url_formatter::kFormatUrlTrimAfterHost,
net::UnescapeRule::NORMAL, nullptr, nullptr, nullptr);
domain_label_->SetText(domain);
if (preview_image_) {
// Get the largest version of the favicon available.
gfx::ImageSkia max_favicon = gfx::ImageSkia::CreateFrom1xBitmap(
data.favicon.GetRepresentation(data.favicon.GetMaxSupportedScale())
.GetBitmap());
preview_image_->SetImage(max_favicon);
const gfx::Size favicon_size = max_favicon.size();
// Scale the favicon to an appropriate size for the tab hover card.
//
// This is reasonably aesthetic for favicons, though it does not necessarily
// fill up the entire width of the hover card. When we move to using
// og:images or screenshots, we'll have to do something more sophisticated.
if (!favicon_size.IsEmpty()) {
float scale = float{kTabHoverCardPreviewImageSize.height()} /
float{favicon_size.height()};
preview_image_->SetImageSize(
gfx::Size(std::roundf(scale * favicon_size.width()),
kTabHoverCardPreviewImageSize.height()));
}
}
}
gfx::Size TabHoverCardBubbleView::CalculatePreferredSize() const {
const gfx::Size size = BubbleDialogDelegateView::CalculatePreferredSize();
return gfx::Size(kPreferredTabHoverCardWidth, size.height());
}