Skip to content

Commit

Permalink
Add PictureInPictureBrowserFrameView for PiP 2.0
Browse files Browse the repository at this point in the history
This is an initial commit to add a new subclass of
BrowserNonClientFrameView for the Picture-in-Picture 2.0 window. This
allows us to have a consistent PiP UI across all the platforms while it
is backed by the browser.

This frame view is currently only added and tested on Linux, and still
has many TODOs which are bugs to be fixed in follow-up CLs.

Bug: 1346734
Change-Id: Ic81572c366cb7f52990081a405cef79752a44200
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3773920
Reviewed-by: Scott Violet <sky@chromium.org>
Commit-Queue: Yiren Wang <yrw@chromium.org>
Reviewed-by: Peter Kasting <pkasting@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1028514}
  • Loading branch information
Yiren Wang authored and Chromium LUCI CQ committed Jul 26, 2022
1 parent 4124de1 commit 27bbedd
Show file tree
Hide file tree
Showing 7 changed files with 375 additions and 0 deletions.
1 change: 1 addition & 0 deletions chrome/app/vector_icons/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ aggregate_vector_icons("chrome_vector_icons") {
"autofill/webauthn_dialog_header.icon",
"autofill/webauthn_dialog_header_dark.icon",
"back_arrow_touch.icon",
"back_to_tab.icon",
"backspace.icon",
"blocked_redirect.icon",
"bookmarkbar_touch_overflow.icon",
Expand Down
43 changes: 43 additions & 0 deletions chrome/app/vector_icons/back_to_tab.icon
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright 2022 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.

CANVAS_DIMENSIONS, 16,
MOVE_TO, 5.2f, 12.14f,
V_LINE_TO, 13.4f,
R_ARC_TO, 1, 1, 0, 0, 0, 1, 1,
R_H_LINE_TO, 6,
R_ARC_TO, 1, 1, 0, 0, 0, 1, -1,
V_LINE_TO, 9,
R_ARC_TO, 1, 1, 0, 0, 0, -1, -1,
R_H_LINE_TO, -6,
R_ARC_TO, 1, 1, 0, 0, 0, -1, 1,
R_V_LINE_TO, 1.84f,
H_LINE_TO, 2.13f,
V_LINE_TO, 7.6f,
H_LINE_TO, 0.8f,
R_V_LINE_TO, 3.21f,
R_CUBIC_TO, 0, 0.73f, 0.6f, 1.33f, 1.33f, 1.33f,
R_H_LINE_TO, 3.07f,
CLOSE,
R_MOVE_TO, 6.67f, -9.21f,
V_LINE_TO, 6.8f,
H_LINE_TO, 13.2f,
V_LINE_TO, 2.93f,
R_CUBIC_TO, 0, -0.73f, -0.59f, -1.33f, -1.33f, -1.33f,
H_LINE_TO, 6.8f,
R_V_LINE_TO, 1.33f,
R_H_LINE_TO, 5.07f,
CLOSE,
NEW_PATH,
MOVE_TO, 5.48f, 2.93f,
V_LINE_TO, 1.6f,
H_LINE_TO, 0.82f,
R_V_LINE_TO, 4.67f,
H_LINE_TO, 2.15f,
V_LINE_TO, 3.87f,
R_LINE_TO, 4.05f, 4.05f,
R_CUBIC_TO, 0.2f, -0.41f, 0.53f, -0.74f, 0.95f, -0.93f,
LINE_TO, 3.09f, 2.93f,
R_H_LINE_TO, 2.39f,
CLOSE
2 changes: 2 additions & 0 deletions chrome/browser/ui/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -4420,6 +4420,8 @@ static_library("ui") {
"views/frame/native_browser_frame.h",
"views/frame/native_browser_frame_factory.cc",
"views/frame/native_browser_frame_factory.h",
"views/frame/picture_in_picture_browser_frame_view.cc",
"views/frame/picture_in_picture_browser_frame_view.h",
"views/frame/system_menu_model_builder.cc",
"views/frame/system_menu_model_builder.h",
"views/frame/system_menu_model_delegate.cc",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,12 @@ bool BrowserDesktopWindowTreeHostLinux::SupportsClientFrameShadow() const {
}

void BrowserDesktopWindowTreeHostLinux::UpdateFrameHints() {
if (native_frame_->browser_view()->browser()->is_type_picture_in_picture()) {
// TODO(https://crbug.com/1346734): Figure out whether or not we need to and
// how to set shades for pip window. Skip casting the view for linux below.
return;
}

auto* view = static_cast<BrowserFrameViewLinux*>(
native_frame_->browser_frame()->GetFrameView());
auto* layout = view->layout();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/frame/opaque_browser_frame_view.h"
#include "chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h"
#include "chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.h"

#if BUILDFLAG(IS_WIN)
#include "chrome/browser/ui/views/frame/glass_browser_frame_view.h"
Expand Down Expand Up @@ -72,6 +73,14 @@ std::unique_ptr<OpaqueBrowserFrameView> CreateOpaqueBrowserFrameView(
std::unique_ptr<BrowserNonClientFrameView> CreateBrowserNonClientFrameView(
BrowserFrame* frame,
BrowserView* browser_view) {
// TODO(https://crbug.com/1346734): Enable it on all platforms.
#if BUILDFLAG(IS_LINUX)
if (browser_view->browser()->is_type_picture_in_picture()) {
return std::make_unique<PictureInPictureBrowserFrameView>(frame,
browser_view);
}
#endif

#if BUILDFLAG(IS_WIN)
if (frame->ShouldUseNativeFrame())
return std::make_unique<GlassBrowserFrameView>(frame, browser_view);
Expand Down
245 changes: 245 additions & 0 deletions chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
// Copyright 2022 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/frame/picture_in_picture_browser_frame_view.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/ui/color/chrome_color_id.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/overlay/overlay_window_image_button.h"
#include "chrome/grit/generated_resources.h"
#include "components/vector_icons/vector_icons.h"
#include "content/public/browser/web_contents.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/hit_test.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/models/image_model.h"
#include "ui/compositor/layer.h"
#include "ui/views/widget/widget_delegate.h"
#include "ui/views/window/window_shape.h"

namespace {

// TODO(https://crbug.com/1346734): Check whether any of the below should be
// based on platform constants instead.

constexpr int kWindowIconImageSize = 14;
constexpr int kBackToTabImageSize = 14;

// The height of the controls bar at the top of the window.
constexpr int kTopControlsHeight = 30;

constexpr int kWindowBorderThickness = 5;
constexpr int kResizeAreaCornerSize = 10;

constexpr int kMinWindowWidth = 500;

class BackToTabButton : public OverlayWindowImageButton {
public:
METADATA_HEADER(BackToTabButton);

explicit BackToTabButton(PressedCallback callback)
: OverlayWindowImageButton(std::move(callback)) {
SetImageModel(
views::Button::STATE_NORMAL,
ui::ImageModel::FromVectorIcon(
kBackToTabIcon, kColorPipWindowForeground, kBackToTabImageSize));

const std::u16string back_to_tab_button_label = l10n_util::GetStringUTF16(
IDS_PICTURE_IN_PICTURE_BACK_TO_TAB_CONTROL_TEXT);
SetTooltipText(back_to_tab_button_label);
}
BackToTabButton(const BackToTabButton&) = delete;
BackToTabButton& operator=(const BackToTabButton&) = delete;
~BackToTabButton() override = default;
};

BEGIN_METADATA(BackToTabButton, OverlayWindowImageButton)
END_METADATA

} // namespace

PictureInPictureBrowserFrameView::PictureInPictureBrowserFrameView(
BrowserFrame* frame,
BrowserView* browser_view)
: BrowserNonClientFrameView(frame, browser_view) {
// Creates a window background with solid color.
// TODO(https://crbug.com/1346734): Need to figure out how to make this
// background color not overlap pip content. AddChildView() would cause it to
// overlap while now it never shows.
window_background_view_ = browser_view->contents_web_view()->AddChildViewAt(
std::make_unique<views::View>(), 0);
window_background_view_->SetPaintToLayer(ui::LAYER_SOLID_COLOR);
window_background_view_->layer()->SetName("WindowBackgroundView");

// Creates a view that will hold all the control views.
AddChildView(
views::Builder<views::BoxLayoutView>()
.CopyAddressTo(&controls_container_view_)
.SetOrientation(views::BoxLayout::Orientation::kHorizontal)
.SetCrossAxisAlignment(views::BoxLayout::CrossAxisAlignment::kCenter)
.Build());
controls_container_view_->SetPaintToLayer(ui::LAYER_SOLID_COLOR);
controls_container_view_->layer()->SetName("ControlsContainerView");

// Creates the window icon.
controls_container_view_->AddChildView(
views::Builder<views::ImageView>()
.CopyAddressTo(&window_icon_)
.SetImage(ui::ImageModel::FromVectorIcon(
vector_icons::kHttpsValidIcon, kColorOmniboxSecurityChipSecure,
kWindowIconImageSize))
.Build());
window_icon_->SetPaintToLayer(ui::LAYER_TEXTURED);
window_icon_->layer()->SetFillsBoundsOpaquely(false);
window_icon_->layer()->SetName("WindowIcon");

// Creates the window title.
controls_container_view_->AddChildView(
views::Builder<views::Label>()
.CopyAddressTo(&window_title_)
.SetText(browser_view->GetWindowTitle())
.SetHorizontalAlignment(gfx::ALIGN_LEFT)
.Build());
controls_container_view_->SetFlexForView(window_title_, 1);
window_title_->SetPaintToLayer(ui::LAYER_TEXTURED);
window_title_->layer()->SetName("WindowTitle");

// Creates the back to tab button.
back_to_tab_button_ = controls_container_view_->AddChildView(
std::make_unique<BackToTabButton>(base::BindRepeating(
[](PictureInPictureBrowserFrameView* frame_view) {
// TODO(https://crbug.com/1346734): Implement functionality.
},
base::Unretained(this))));
back_to_tab_button_->SetPaintToLayer(ui::LAYER_TEXTURED);
back_to_tab_button_->layer()->SetFillsBoundsOpaquely(false);
back_to_tab_button_->layer()->SetName("BackToTabButton");

// Creates the close button.
close_image_button_ = controls_container_view_->AddChildView(
std::make_unique<CloseImageButton>(base::BindRepeating(
[](PictureInPictureBrowserFrameView* frame_view) {
frame_view->frame()->CloseWithReason(
views::Widget::ClosedReason::kCloseButtonClicked);
},
base::Unretained(this))));
close_image_button_->SetPaintToLayer(ui::LAYER_TEXTURED);
close_image_button_->layer()->SetFillsBoundsOpaquely(false);
close_image_button_->layer()->SetName("CloseImageButton");
}

///////////////////////////////////////////////////////////////////////////////
// BrowserNonClientFrameView implementations:

gfx::Rect PictureInPictureBrowserFrameView::GetBoundsForTabStripRegion(
const gfx::Size& tabstrip_minimum_size) const {
return gfx::Rect();
}

int PictureInPictureBrowserFrameView::GetTopInset(bool restored) const {
return kTopControlsHeight;
}

int PictureInPictureBrowserFrameView::GetThemeBackgroundXInset() const {
return 0;
}

void PictureInPictureBrowserFrameView::UpdateMinimumSize() {
GetWidget()->OnSizeConstraintsChanged();
}

gfx::Rect PictureInPictureBrowserFrameView::GetBoundsForClientView() const {
return bounds();
}

gfx::Rect PictureInPictureBrowserFrameView::GetWindowBoundsForClientBounds(
const gfx::Rect& client_bounds) const {
return bounds();
}

int PictureInPictureBrowserFrameView::NonClientHitTest(
const gfx::Point& point) {
// Do nothing if the click is outside the window.
if (!bounds().Contains(point))
return HTNOWHERE;

// Allow interacting with the buttons.
if (GetBackToTabControlsBounds().Contains(point) ||
GetCloseControlsBounds().Contains(point))
return HTCLIENT;

// Allow dragging and resizing the window.
int window_component = GetHTComponentForFrame(
point, gfx::Insets(kWindowBorderThickness), kResizeAreaCornerSize,
kResizeAreaCornerSize, GetWidget()->widget_delegate()->CanResize());
if (window_component != HTNOWHERE)
return window_component;

// Allow interacting with the web contents.
int frame_component = frame()->client_view()->NonClientHitTest(point);
if (frame_component != HTNOWHERE)
return frame_component;

return HTCAPTION;
}

void PictureInPictureBrowserFrameView::GetWindowMask(const gfx::Size& size,
SkPath* window_mask) {
DCHECK(window_mask);
views::GetDefaultWindowMask(size, window_mask);
}

void PictureInPictureBrowserFrameView::UpdateWindowTitle() {
window_title_->SchedulePaint();
}

gfx::Size PictureInPictureBrowserFrameView::GetMinimumSize() const {
// TODO(https://crbug.com/1346734): Calculate the size as OverlayWindowViews.
return gfx::Size(kMinWindowWidth, kMinWindowWidth);
}

gfx::Size PictureInPictureBrowserFrameView::GetMaximumSize() const {
// TODO(https://crbug.com/1346734): Calculate the size as OverlayWindowViews.
return browser_view()->GetMaximumSize();
}

void PictureInPictureBrowserFrameView::OnThemeChanged() {
BrowserNonClientFrameView::OnThemeChanged();

const auto* color_provider = GetColorProvider();
window_background_view_->layer()->SetColor(
color_provider->GetColor(kColorPipWindowBackground));
controls_container_view_->layer()->SetColor(
SkColorSetA(color_provider->GetColor(kColorPipWindowControlsBackground),
SK_AlphaOPAQUE));

// Must set an opaque background color for the label before setting opacity.
window_title_->SetBackgroundColor(
color_provider->GetColor(kColorPipWindowControlsBackground));
window_title_->SetEnabledColor(
color_provider->GetColor(kColorPipWindowForeground));
window_title_->layer()->SetFillsBoundsOpaquely(false);
}

gfx::Rect PictureInPictureBrowserFrameView::GetBackToTabControlsBounds() const {
DCHECK(back_to_tab_button_);
return back_to_tab_button_->GetMirroredBounds();
}

gfx::Rect PictureInPictureBrowserFrameView::GetCloseControlsBounds() const {
DCHECK(close_image_button_);
return close_image_button_->GetMirroredBounds();
}

void PictureInPictureBrowserFrameView::OnBoundsChanged(
const gfx::Rect& previous_bounds) {
window_background_view_->SetBoundsRect(
gfx::Rect(gfx::Point(), bounds().size()));
controls_container_view_->SetBoundsRect(
gfx::Rect(0, 0, width(), kTopControlsHeight));
}

BEGIN_METADATA(PictureInPictureBrowserFrameView, BrowserNonClientFrameView)
END_METADATA

0 comments on commit 27bbedd

Please sign in to comment.