Commit 3ac20c70 authored by Jennifer Apacible's avatar Jennifer Apacible Committed by Commit Bot

[Picture in Picture] Add media controls on window.

This change adds media controls (play/pause, close) on the
Picture-in-Picture window for desktop (OverlayWindowViews). This adds
three vectorized icons used for these controls. These are currently
rendered as white when the user mouses over the window, and disappears
when the mouse is not hovering over the window.

The intended sizes of the icons are currently hardcoded. They will be
updated when we've determined how we want them to be sized or scaled.

Future work includes making the controls accessible and handling events
triggered by keyboard or touch inputs.

BUG: 836389
Change-Id: I37987286b531c1ce1ddcf867f72510194657053d
Reviewed-on: https://chromium-review.googlesource.com/1026519Reviewed-by: default avatarMichael Wasserman <msw@chromium.org>
Reviewed-by: default avatarMounir Lamouri <mlamouri@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Commit-Queue: apacible <apacible@chromium.org>
Cr-Commit-Position: refs/heads/master@{#553732}
parent c0a8f3ee
......@@ -10,10 +10,14 @@
#include "chrome/grit/generated_resources.h"
#include "content/public/browser/picture_in_picture_window_controller.h"
#include "media/base/video_util.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/hit_test.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/screen.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/vector_icons.h"
#include "ui/views/widget/widget_delegate.h"
#include "ui/views/window/non_client_view.h"
......@@ -26,6 +30,11 @@ std::unique_ptr<content::OverlayWindow> content::OverlayWindow::Create(
namespace {
const int kBorderThickness = 5;
const int kResizeAreaCornerSize = 16;
// TODO(apacible): Update sizes per UX feedback and when scaling is determined.
// http://crbug.com/836389
const gfx::Size kCloseIconSize = gfx::Size(50, 50);
const gfx::Size kPlayPauseIconSize = gfx::Size(120, 120);
} // namespace
// OverlayWindow implementation of NonClientFrameView.
......@@ -97,7 +106,10 @@ class OverlayWindowWidgetDelegate : public views::WidgetDelegate {
OverlayWindowViews::OverlayWindowViews(
content::PictureInPictureWindowController* controller)
: controller_(controller) {
: controller_(controller),
video_view_(new views::View()),
close_controls_view_(new views::ImageButton(nullptr)),
play_pause_controls_view_(new views::ToggleImageButton(nullptr)) {
views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.bounds = CalculateAndUpdateBounds();
......@@ -109,6 +121,7 @@ OverlayWindowViews::OverlayWindowViews(
params.delegate = new OverlayWindowWidgetDelegate(this);
Init(params);
SetUpViews();
}
OverlayWindowViews::~OverlayWindowViews() = default;
......@@ -157,6 +170,40 @@ gfx::Rect OverlayWindowViews::CalculateAndUpdateBounds() {
current_size_);
}
void OverlayWindowViews::SetUpViews() {
// Set up views::View that closes the window.
close_controls_view_->SetSize(kCloseIconSize);
close_controls_view_->SetImageAlignment(views::ImageButton::ALIGN_CENTER,
views::ImageButton::ALIGN_MIDDLE);
close_controls_view_->SetImage(
views::Button::STATE_NORMAL,
gfx::CreateVectorIcon(views::kPictureInPictureCloseIcon, SK_ColorWHITE));
// Set up views::View that toggles play/pause.
play_pause_controls_view_->SetSize(kPlayPauseIconSize);
play_pause_controls_view_->SetImageAlignment(
views::ImageButton::ALIGN_CENTER, views::ImageButton::ALIGN_MIDDLE);
play_pause_controls_view_->SetImage(
views::Button::STATE_NORMAL,
gfx::CreateVectorIcon(views::kPictureInPicturePlayIcon,
kPlayPauseIconSize.width(), SK_ColorWHITE));
gfx::ImageSkia pause_icon =
gfx::CreateVectorIcon(views::kPictureInPicturePauseIcon,
kPlayPauseIconSize.width(), SK_ColorWHITE);
play_pause_controls_view_->SetToggledImage(views::Button::STATE_NORMAL,
&pause_icon);
play_pause_controls_view_->SetToggled(false);
// Paint to ui::Layers to use in the OverlaySurfaceEmbedder.
video_view_->SetPaintToLayer(ui::LAYER_TEXTURED);
close_controls_view_->SetPaintToLayer(ui::LAYER_TEXTURED);
play_pause_controls_view_->SetPaintToLayer(ui::LAYER_TEXTURED);
// Don't show the controls until the mouse hovers over the window.
GetCloseControlsLayer()->SetVisible(false);
GetPlayPauseControlsLayer()->SetVisible(false);
}
bool OverlayWindowViews::IsActive() const {
return views::Widget::IsActive();
}
......@@ -193,6 +240,32 @@ void OverlayWindowViews::UpdateVideoSize(const gfx::Size& natural_size) {
SetBounds(CalculateAndUpdateBounds());
}
ui::Layer* OverlayWindowViews::GetVideoLayer() {
return video_view_->layer();
}
ui::Layer* OverlayWindowViews::GetCloseControlsLayer() {
return close_controls_view_->layer();
}
ui::Layer* OverlayWindowViews::GetPlayPauseControlsLayer() {
return play_pause_controls_view_->layer();
}
gfx::Rect OverlayWindowViews::GetCloseControlsBounds() {
return gfx::Rect(
gfx::Point(GetBounds().size().width() - kCloseIconSize.width(), 0),
kCloseIconSize);
}
gfx::Rect OverlayWindowViews::GetPlayPauseControlsBounds() {
return gfx::Rect(
gfx::Point(
(GetBounds().size().width() - kPlayPauseIconSize.width()) / 2,
(GetBounds().size().height() - kPlayPauseIconSize.height()) / 2),
kPlayPauseIconSize);
}
gfx::Size OverlayWindowViews::GetMinimumSize() const {
return min_size_;
}
......@@ -208,15 +281,38 @@ void OverlayWindowViews::OnNativeWidgetWorkspaceChanged() {
}
void OverlayWindowViews::OnMouseEvent(ui::MouseEvent* event) {
if (event->type() != ui::ET_MOUSE_RELEASED)
return;
if (event->IsOnlyLeftMouseButton()) {
controller_->TogglePlayPause();
event->SetHandled();
} else if (event->IsOnlyRightMouseButton()) {
controller_->Close();
event->SetHandled();
// TODO(apacible): Handle tab focusing and touch screen events.
// http://crbug.com/836389
switch (event->type()) {
// Only show the media controls when the mouse is hovering over the window.
case ui::ET_MOUSE_ENTERED:
GetCloseControlsLayer()->SetVisible(true);
GetPlayPauseControlsLayer()->SetVisible(true);
break;
case ui::ET_MOUSE_EXITED:
GetCloseControlsLayer()->SetVisible(false);
GetPlayPauseControlsLayer()->SetVisible(false);
break;
case ui::ET_MOUSE_RELEASED:
if (!event->IsOnlyLeftMouseButton())
return;
// TODO(apacible): Clip the clickable areas to where the button icons are
// drawn. http://crbug.com/836389
if (GetCloseControlsBounds().Contains(event->location())) {
controller_->Close();
event->SetHandled();
} else if (GetPlayPauseControlsBounds().Contains(event->location())) {
bool is_active = controller_->TogglePlayPause();
play_pause_controls_view_->SetToggled(is_active);
event->SetHandled();
}
break;
default:
break;
}
}
......
......@@ -10,6 +10,11 @@
#include "ui/gfx/geometry/size.h"
#include "ui/views/widget/widget.h"
namespace views {
class ImageButton;
class ToggleImageButton;
} // namespace views
// The Chrome desktop implementation of OverlayWindow. This will only be
// implemented in views, which will support all desktop platforms.
class OverlayWindowViews : public content::OverlayWindow, public views::Widget {
......@@ -27,6 +32,11 @@ class OverlayWindowViews : public content::OverlayWindow, public views::Widget {
ui::Layer* GetLayer() override;
gfx::Rect GetBounds() const override;
void UpdateVideoSize(const gfx::Size& natural_size) override;
ui::Layer* GetVideoLayer() override;
ui::Layer* GetCloseControlsLayer() override;
ui::Layer* GetPlayPauseControlsLayer() override;
gfx::Rect GetCloseControlsBounds() override;
gfx::Rect GetPlayPauseControlsBounds() override;
// views::Widget:
gfx::Size GetMinimumSize() const override;
......@@ -44,6 +54,9 @@ class OverlayWindowViews : public content::OverlayWindow, public views::Widget {
// |min_size_| and |max_size_|.
gfx::Rect CalculateAndUpdateBounds();
// Set up the views::Views that will be shown on the window.
void SetUpViews();
// Not owned; |controller_| owns |this|.
content::PictureInPictureWindowController* controller_;
......@@ -61,6 +74,11 @@ class OverlayWindowViews : public content::OverlayWindow, public views::Widget {
// ensuring factors such as aspect ratio is maintained.
gfx::Size natural_size_;
// Views to be shown.
std::unique_ptr<views::View> video_view_;
std::unique_ptr<views::ImageButton> close_controls_view_;
std::unique_ptr<views::ToggleImageButton> play_pause_controls_view_;
DISALLOW_COPY_AND_ASSIGN(OverlayWindowViews);
};
......
......@@ -11,17 +11,19 @@ namespace content {
OverlaySurfaceEmbedder::OverlaySurfaceEmbedder(OverlayWindow* window)
: window_(window) {
DCHECK(window_);
surface_layer_ = std::make_unique<ui::Layer>(ui::LAYER_TEXTURED);
surface_layer_->SetMasksToBounds(true);
video_layer_ = window_->GetVideoLayer();
video_layer_->SetMasksToBounds(true);
// The frame provided by the parent window's layer needs to show through
// |surface_layer_|.
surface_layer_->SetFillsBoundsOpaquely(false);
// |surface_layer_| bounds are set with the (0, 0) origin point. The
// |video_layer_|.
video_layer_->SetFillsBoundsOpaquely(false);
// |video_layer_| bounds are set with the (0, 0) origin point. The
// positioning of |window_| is dictated by itself.
surface_layer_->SetBounds(
video_layer_->SetBounds(
gfx::Rect(gfx::Point(0, 0), window_->GetBounds().size()));
window_->GetLayer()->Add(surface_layer_.get());
window_->GetLayer()->Add(video_layer_);
AddControlsLayers();
}
OverlaySurfaceEmbedder::~OverlaySurfaceEmbedder() = default;
......@@ -29,16 +31,34 @@ OverlaySurfaceEmbedder::~OverlaySurfaceEmbedder() = default;
void OverlaySurfaceEmbedder::SetPrimarySurfaceId(
const viz::SurfaceId& surface_id) {
// SurfaceInfo has information about the embedded surface.
surface_layer_->SetShowPrimarySurface(
video_layer_->SetShowPrimarySurface(
surface_id, window_->GetBounds().size(), SK_ColorBLACK,
cc::DeadlinePolicy::UseDefaultDeadline(),
true /* stretch_content_to_fill_bounds */);
}
void OverlaySurfaceEmbedder::UpdateLayerBounds() {
surface_layer_->SetBounds(
gfx::Rect(gfx::Point(0, 0), window_->GetBounds().size()));
surface_layer_->SetSurfaceSize(window_->GetBounds().size());
// Update the size and position of the video to stretch on the entire window.
gfx::Size window_size = window_->GetBounds().size();
video_layer_->SetBounds(gfx::Rect(gfx::Point(0, 0), window_size));
video_layer_->SetSurfaceSize(window_size);
// Update the size and position of controls.
close_controls_layer_->SetBounds(window_->GetCloseControlsBounds());
play_pause_controls_layer_->SetBounds(window_->GetPlayPauseControlsBounds());
}
void OverlaySurfaceEmbedder::AddControlsLayers() {
close_controls_layer_ = window_->GetCloseControlsLayer();
close_controls_layer_->SetFillsBoundsOpaquely(false);
close_controls_layer_->SetBounds(window_->GetCloseControlsBounds());
play_pause_controls_layer_ = window_->GetPlayPauseControlsLayer();
play_pause_controls_layer_->SetFillsBoundsOpaquely(false);
play_pause_controls_layer_->SetBounds(window_->GetPlayPauseControlsBounds());
window_->GetLayer()->Add(close_controls_layer_);
window_->GetLayer()->Add(play_pause_controls_layer_);
}
} // namespace content
......@@ -25,6 +25,7 @@ class OverlaySurfaceEmbedder {
void SetPrimarySurfaceId(const viz::SurfaceId& surface_id);
void UpdateLayerBounds();
void AddControlsLayers();
private:
// The window which embeds the client. Weak pointer since the
......@@ -34,6 +35,11 @@ class OverlaySurfaceEmbedder {
// Contains the client's content.
std::unique_ptr<ui::Layer> surface_layer_;
// Owned by the OverlayWindow implementation.
ui::Layer* video_layer_ = nullptr;
ui::Layer* close_controls_layer_ = nullptr;
ui::Layer* play_pause_controls_layer_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(OverlaySurfaceEmbedder);
};
......
......@@ -100,7 +100,7 @@ void PictureInPictureWindowControllerImpl::UpdateLayerBounds() {
embedder_->UpdateLayerBounds();
}
void PictureInPictureWindowControllerImpl::TogglePlayPause() {
bool PictureInPictureWindowControllerImpl::TogglePlayPause() {
DCHECK(window_ && window_->IsActive());
content::MediaWebContentsObserver* observer =
......@@ -113,10 +113,12 @@ void PictureInPictureWindowControllerImpl::TogglePlayPause() {
if (observer->IsPlayerActive(*player_id)) {
player_id->first->Send(new MediaPlayerDelegateMsg_Pause(
player_id->first->GetRoutingID(), player_id->second));
} else {
player_id->first->Send(new MediaPlayerDelegateMsg_Play(
player_id->first->GetRoutingID(), player_id->second));
return false;
}
player_id->first->Send(new MediaPlayerDelegateMsg_Play(
player_id->first->GetRoutingID(), player_id->second));
return true;
}
} // namespace content
\ No newline at end of file
......@@ -39,7 +39,7 @@ class PictureInPictureWindowControllerImpl
const gfx::Size& natural_size) override;
CONTENT_EXPORT OverlayWindow* GetWindowForTesting() override;
CONTENT_EXPORT void UpdateLayerBounds() override;
CONTENT_EXPORT void TogglePlayPause() override;
CONTENT_EXPORT bool TogglePlayPause() override;
private:
friend class WebContentsUserData<PictureInPictureWindowControllerImpl>;
......
......@@ -45,6 +45,15 @@ class OverlayWindow {
virtual gfx::Rect GetBounds() const = 0;
virtual void UpdateVideoSize(const gfx::Size& natural_size) = 0;
// Retrieve the ui::Layers corresponding to the video and controls.
virtual ui::Layer* GetVideoLayer() = 0;
virtual ui::Layer* GetCloseControlsLayer() = 0;
virtual ui::Layer* GetPlayPauseControlsLayer() = 0;
// Retrieves the bounds of the media controls.
virtual gfx::Rect GetCloseControlsBounds() = 0;
virtual gfx::Rect GetPlayPauseControlsBounds() = 0;
private:
DISALLOW_COPY_AND_ASSIGN(OverlayWindow);
};
......
......@@ -41,7 +41,9 @@ class PictureInPictureWindowController {
virtual void UpdateLayerBounds() = 0;
// Commands.
virtual void TogglePlayPause() = 0;
// Returns true if the player is active (i.e. currently playing) after this
// call.
virtual bool TogglePlayPause() = 0;
protected:
// Use PictureInPictureWindowController::GetOrCreateForWebContents() to
......
......@@ -34,6 +34,9 @@ aggregate_vector_icons("views_vector_icons") {
"new_window.icon",
"open.icon",
"options.icon",
"picture_in_picture_close.icon",
"picture_in_picture_pause.icon",
"picture_in_picture_play.icon",
"pin.icon",
"radio_button_active.icon",
"radio_button_normal.icon",
......
// Copyright 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.
CANVAS_DIMENSIONS, 24,
MOVE_TO, 19, 6.41f,
LINE_TO, 17.59f, 5,
LINE_TO, 12, 10.59f,
LINE_TO, 6.41f, 5,
LINE_TO, 5, 6.41f,
LINE_TO, 10.59f, 12,
LINE_TO, 5, 17.59f,
LINE_TO, 6.41f, 19,
LINE_TO, 12, 13.41f,
LINE_TO, 17.59f, 19,
LINE_TO, 19, 17.59f,
LINE_TO, 13.41f, 12,
CLOSE
\ No newline at end of file
// Copyright 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.
CANVAS_DIMENSIONS, 24,
MOVE_TO, 12, 2,
CUBIC_TO, 6.48f, 2, 2, 6.48f, 2, 12,
R_CUBIC_TO, 0, 5.52f, 4.48f, 10, 10, 10,
R_CUBIC_TO, 5.52f, 0, 10, -4.48f, 10, -10,
CUBIC_TO_SHORTHAND, 17.52f, 2, 12, 2,
CLOSE,
R_MOVE_TO, -1, 14,
H_LINE_TO, 9,
V_LINE_TO, 8,
R_H_LINE_TO, 2,
R_V_LINE_TO, 8,
CLOSE,
R_MOVE_TO, 4, 0,
R_H_LINE_TO, -2,
V_LINE_TO, 8,
R_H_LINE_TO, 2,
R_V_LINE_TO, 8,
CLOSE
\ No newline at end of file
// Copyright 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.
CANVAS_DIMENSIONS, 24,
MOVE_TO, 12, 2,
CUBIC_TO, 6.48f, 2, 2, 6.48f, 2, 12,
R_CUBIC_TO, 0, 5.52f, 4.48f, 10, 10, 10,
R_CUBIC_TO, 5.52f, 0, 10, -4.48f, 10, -10,
CUBIC_TO_SHORTHAND, 17.52f, 2, 12, 2,
CLOSE,
R_MOVE_TO, -2, 14.5f,
R_V_LINE_TO, -9,
R_LINE_TO, 6, 4.5f,
R_LINE_TO, -6, 4.5f,
CLOSE
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment