Commit 28e9c18c authored by François Beaufort's avatar François Beaufort Committed by Commit Bot

Add replay button to Picture-in-Picture window when video ends

This CL makes sure there's a new replay button visible in the PiP window
when video ends. This icon comes from Material Icons library at
https://material.io/tools/icons/?icon=replay.

https://i.imgur.com/I1n90tM.png

Bug: 911620

Change-Id: Ibfcb96b9c69228ad74afe96ec00eb0e28d335ad8
Reviewed-on: https://chromium-review.googlesource.com/c/1360852
Commit-Queue: François Beaufort <beaufort.francois@gmail.com>
Reviewed-by: default avatarMounir Lamouri <mlamouri@chromium.org>
Reviewed-by: default avatarEvan Stade <estade@chromium.org>
Cr-Commit-Position: refs/heads/master@{#615846}
parent 3b571837
...@@ -5597,6 +5597,9 @@ the Bookmarks menu."> ...@@ -5597,6 +5597,9 @@ the Bookmarks menu.">
<message name="IDS_PICTURE_IN_PICTURE_PLAY_CONTROL_TEXT" desc="Text label of the play control button. The button appears when the user hovers over the Picture-in-Picture window and the video is currently paused."> <message name="IDS_PICTURE_IN_PICTURE_PLAY_CONTROL_TEXT" desc="Text label of the play control button. The button appears when the user hovers over the Picture-in-Picture window and the video is currently paused.">
Play Play
</message> </message>
<message name="IDS_PICTURE_IN_PICTURE_REPLAY_CONTROL_TEXT" desc="Text label of the replay control button. The button appears when the user hovers over the Picture-in-Picture window and the video is ended.">
Play from the beginning
</message>
<message name="IDS_PICTURE_IN_PICTURE_CLOSE_CONTROL_TEXT" desc="Text label of the close control button. The button appears when the user hovers over the Picture-in-Picture window."> <message name="IDS_PICTURE_IN_PICTURE_CLOSE_CONTROL_TEXT" desc="Text label of the close control button. The button appears when the user hovers over the Picture-in-Picture window.">
Close Close
</message> </message>
......
...@@ -1349,8 +1349,8 @@ IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest, ...@@ -1349,8 +1349,8 @@ IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
OverlayWindowViews* overlay_window = static_cast<OverlayWindowViews*>( OverlayWindowViews* overlay_window = static_cast<OverlayWindowViews*>(
window_controller()->GetWindowForTesting()); window_controller()->GetWindowForTesting());
EXPECT_TRUE(overlay_window->play_pause_controls_view_for_testing() EXPECT_EQ(overlay_window->playback_state_for_testing(),
->toggled_for_testing()); OverlayWindowViews::PlaybackState::kPlaying);
ASSERT_TRUE( ASSERT_TRUE(
content::ExecuteScript(active_web_contents, "exitPictureInPicture();")); content::ExecuteScript(active_web_contents, "exitPictureInPicture();"));
...@@ -1376,8 +1376,8 @@ IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest, ...@@ -1376,8 +1376,8 @@ IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
OverlayWindowViews* overlay_window = static_cast<OverlayWindowViews*>( OverlayWindowViews* overlay_window = static_cast<OverlayWindowViews*>(
window_controller()->GetWindowForTesting()); window_controller()->GetWindowForTesting());
EXPECT_FALSE(overlay_window->play_pause_controls_view_for_testing() EXPECT_EQ(overlay_window->playback_state_for_testing(),
->toggled_for_testing()); OverlayWindowViews::PlaybackState::kPaused);
} }
} }
......
...@@ -2613,6 +2613,8 @@ jumbo_split_static_library("ui") { ...@@ -2613,6 +2613,8 @@ jumbo_split_static_library("ui") {
"views/overlay/control_image_button.h", "views/overlay/control_image_button.h",
"views/overlay/overlay_window_views.cc", "views/overlay/overlay_window_views.cc",
"views/overlay/overlay_window_views.h", "views/overlay/overlay_window_views.h",
"views/overlay/playback_image_button.cc",
"views/overlay/playback_image_button.h",
"views/overlay/resize_handle_button.cc", "views/overlay/resize_handle_button.cc",
"views/overlay/resize_handle_button.h", "views/overlay/resize_handle_button.h",
"views/page_action/page_action_icon_container_view.cc", "views/page_action/page_action_icon_container_view.cc",
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "chrome/app/vector_icons/vector_icons.h" #include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/ui/views/overlay/close_image_button.h" #include "chrome/browser/ui/views/overlay/close_image_button.h"
#include "chrome/browser/ui/views/overlay/control_image_button.h" #include "chrome/browser/ui/views/overlay/control_image_button.h"
#include "chrome/browser/ui/views/overlay/playback_image_button.h"
#include "chrome/browser/ui/views/overlay/resize_handle_button.h" #include "chrome/browser/ui/views/overlay/resize_handle_button.h"
#include "chrome/grit/generated_resources.h" #include "chrome/grit/generated_resources.h"
#include "components/vector_icons/vector_icons.h" #include "components/vector_icons/vector_icons.h"
...@@ -186,7 +187,7 @@ OverlayWindowViews::OverlayWindowViews( ...@@ -186,7 +187,7 @@ OverlayWindowViews::OverlayWindowViews(
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
resize_handle_view_(new views::ResizeHandleButton(this)), resize_handle_view_(new views::ResizeHandleButton(this)),
#endif #endif
play_pause_controls_view_(new views::ToggleImageButton(this)), play_pause_controls_view_(new views::PlaybackImageButton(this)),
hide_controls_timer_( hide_controls_timer_(
FROM_HERE, FROM_HERE,
base::TimeDelta::FromMilliseconds(2500 /* 2.5 seconds */), base::TimeDelta::FromMilliseconds(2500 /* 2.5 seconds */),
...@@ -335,10 +336,9 @@ void OverlayWindowViews::SetUpViews() { ...@@ -335,10 +336,9 @@ void OverlayWindowViews::SetUpViews() {
video_view_->SetPaintToLayer(ui::LAYER_TEXTURED); video_view_->SetPaintToLayer(ui::LAYER_TEXTURED);
video_view_->layer()->set_name("VideoView"); video_view_->layer()->set_name("VideoView");
// views::View that toggles play/pause. ------------------------------------- // views::View that toggles play/pause/replay. ------------------------------
play_pause_controls_view_->SetImageAlignment( play_pause_controls_view_->SetPlaybackState(
views::ImageButton::ALIGN_CENTER, views::ImageButton::ALIGN_MIDDLE); controller_->IsPlayerActive() ? kPlaying : kPaused);
play_pause_controls_view_->SetToggled(controller_->IsPlayerActive());
play_pause_controls_view_->set_owned_by_client(); play_pause_controls_view_->set_owned_by_client();
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
...@@ -349,21 +349,6 @@ void OverlayWindowViews::SetUpViews() { ...@@ -349,21 +349,6 @@ void OverlayWindowViews::SetUpViews() {
resize_handle_view_->set_owned_by_client(); resize_handle_view_->set_owned_by_client();
#endif #endif
// Accessibility.
play_pause_controls_view_->SetFocusForPlatform(); // Make button focusable.
const base::string16 play_pause_accessible_button_label(
l10n_util::GetStringUTF16(
IDS_PICTURE_IN_PICTURE_PLAY_PAUSE_CONTROL_ACCESSIBLE_TEXT));
play_pause_controls_view_->SetAccessibleName(
play_pause_accessible_button_label);
const base::string16 play_button_label(
l10n_util::GetStringUTF16(IDS_PICTURE_IN_PICTURE_PLAY_CONTROL_TEXT));
play_pause_controls_view_->SetTooltipText(play_button_label);
const base::string16 pause_button_label(
l10n_util::GetStringUTF16(IDS_PICTURE_IN_PICTURE_PAUSE_CONTROL_TEXT));
play_pause_controls_view_->SetToggledTooltipText(pause_button_label);
play_pause_controls_view_->SetInstallFocusRingOnFocus(true);
// Set up view::Views heirarchy. -------------------------------------------- // Set up view::Views heirarchy. --------------------------------------------
controls_parent_view_->AddChildView(play_pause_controls_view_.get()); controls_parent_view_->AddChildView(play_pause_controls_view_.get());
GetContentsView()->AddChildView(controls_scrim_view_.get()); GetContentsView()->AddChildView(controls_scrim_view_.get());
...@@ -408,16 +393,13 @@ void OverlayWindowViews::UpdateControlsVisibility(bool is_visible) { ...@@ -408,16 +393,13 @@ void OverlayWindowViews::UpdateControlsVisibility(bool is_visible) {
if (always_hide_play_pause_button_ && is_visible) if (always_hide_play_pause_button_ && is_visible)
play_pause_controls_view_->SetVisible(false); play_pause_controls_view_->SetVisible(false);
GetControlsScrimLayer()->SetVisible(is_visible);
GetControlsParentLayer()->SetVisible(is_visible);
GetCloseControlsLayer()->SetVisible(is_visible); GetCloseControlsLayer()->SetVisible(is_visible);
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
GetResizeHandleLayer()->SetVisible(is_visible); GetResizeHandleLayer()->SetVisible(is_visible);
#endif #endif
GetControlsScrimLayer()->SetVisible(
(playback_state_ == kEndOfVideo) ? false : is_visible);
GetControlsParentLayer()->SetVisible(
(playback_state_ == kEndOfVideo) ? false : is_visible);
} }
void OverlayWindowViews::UpdateControlsBounds() { void OverlayWindowViews::UpdateControlsBounds() {
...@@ -481,19 +463,7 @@ void OverlayWindowViews::UpdateCustomControlsSize( ...@@ -481,19 +463,7 @@ void OverlayWindowViews::UpdateCustomControlsSize(
void OverlayWindowViews::UpdatePlayPauseControlsSize() { void OverlayWindowViews::UpdatePlayPauseControlsSize() {
UpdateButtonSize(); UpdateButtonSize();
play_pause_controls_view_->SetSize(button_size_); play_pause_controls_view_->SetButtonSize(button_size_);
play_pause_controls_view_->SetImage(
views::Button::STATE_NORMAL,
gfx::CreateVectorIcon(vector_icons::kPlayArrowIcon,
button_size_.width() / 2, kControlIconColor));
gfx::ImageSkia pause_icon = gfx::CreateVectorIcon(
vector_icons::kPauseIcon, button_size_.width() / 2, kControlIconColor);
play_pause_controls_view_->SetToggledImage(views::Button::STATE_NORMAL,
&pause_icon);
const gfx::ImageSkia play_pause_background = gfx::CreateVectorIcon(
kPictureInPictureControlBackgroundIcon, button_size_.width(), kBgColor);
play_pause_controls_view_->SetBackgroundImage(
kBgColor, &play_pause_background, &play_pause_background);
} }
void OverlayWindowViews::CreateCustomControl( void OverlayWindowViews::CreateCustomControl(
...@@ -625,28 +595,8 @@ void OverlayWindowViews::UpdateVideoSize(const gfx::Size& natural_size) { ...@@ -625,28 +595,8 @@ void OverlayWindowViews::UpdateVideoSize(const gfx::Size& natural_size) {
} }
void OverlayWindowViews::SetPlaybackState(PlaybackState playback_state) { void OverlayWindowViews::SetPlaybackState(PlaybackState playback_state) {
// TODO(apacible): have machine state for controls visibility. playback_state_for_testing_ = playback_state;
bool controls_parent_layer_visible = GetControlsParentLayer()->visible(); play_pause_controls_view_->SetPlaybackState(playback_state);
playback_state_ = playback_state;
switch (playback_state_) {
case kPlaying:
play_pause_controls_view_->SetToggled(true);
controls_parent_view_->SetVisible(true);
GetControlsParentLayer()->SetVisible(controls_parent_layer_visible);
break;
case kPaused:
play_pause_controls_view_->SetToggled(false);
controls_parent_view_->SetVisible(true);
GetControlsParentLayer()->SetVisible(controls_parent_layer_visible);
break;
case kEndOfVideo:
controls_scrim_view_->SetVisible(false);
controls_parent_view_->SetVisible(false);
GetControlsParentLayer()->SetVisible(false);
break;
}
} }
void OverlayWindowViews::SetAlwaysHidePlayPauseButton(bool is_visible) { void OverlayWindowViews::SetAlwaysHidePlayPauseButton(bool is_visible) {
...@@ -903,10 +853,10 @@ void OverlayWindowViews::TogglePlayPause() { ...@@ -903,10 +853,10 @@ void OverlayWindowViews::TogglePlayPause() {
// TogglePlayPause() since the IPC message may not have been propogated // TogglePlayPause() since the IPC message may not have been propogated
// the media player yet. // the media player yet.
bool is_active = controller_->TogglePlayPause(); bool is_active = controller_->TogglePlayPause();
play_pause_controls_view_->SetToggled(is_active); play_pause_controls_view_->SetPlaybackState(is_active ? kPlaying : kPaused);
} }
views::ToggleImageButton* views::PlaybackImageButton*
OverlayWindowViews::play_pause_controls_view_for_testing() const { OverlayWindowViews::play_pause_controls_view_for_testing() const {
return play_pause_controls_view_.get(); return play_pause_controls_view_.get();
} }
...@@ -925,5 +875,5 @@ views::View* OverlayWindowViews::controls_parent_view_for_testing() const { ...@@ -925,5 +875,5 @@ views::View* OverlayWindowViews::controls_parent_view_for_testing() const {
OverlayWindowViews::PlaybackState OverlayWindowViews::PlaybackState
OverlayWindowViews::playback_state_for_testing() const { OverlayWindowViews::playback_state_for_testing() const {
return playback_state_; return playback_state_for_testing_;
} }
...@@ -18,8 +18,8 @@ ...@@ -18,8 +18,8 @@
namespace views { namespace views {
class ControlImageButton; class ControlImageButton;
class CloseImageButton; class CloseImageButton;
class PlaybackImageButton;
class ResizeHandleButton; class ResizeHandleButton;
class ToggleImageButton;
} // namespace views } // namespace views
// The Chrome desktop implementation of OverlayWindow. This will only be // The Chrome desktop implementation of OverlayWindow. This will only be
...@@ -82,7 +82,7 @@ class OverlayWindowViews : public content::OverlayWindow, ...@@ -82,7 +82,7 @@ class OverlayWindowViews : public content::OverlayWindow,
// visible. // visible.
bool AreControlsVisible() const; bool AreControlsVisible() const;
views::ToggleImageButton* play_pause_controls_view_for_testing() const; views::PlaybackImageButton* play_pause_controls_view_for_testing() const;
gfx::Point close_image_position_for_testing() const; gfx::Point close_image_position_for_testing() const;
gfx::Point resize_handle_position_for_testing() const; gfx::Point resize_handle_position_for_testing() const;
views::View* controls_parent_view_for_testing() const; views::View* controls_parent_view_for_testing() const;
...@@ -161,10 +161,6 @@ class OverlayWindowViews : public content::OverlayWindow, ...@@ -161,10 +161,6 @@ class OverlayWindowViews : public content::OverlayWindow,
// case for media streams video that user is not allowed to play/pause. // case for media streams video that user is not allowed to play/pause.
bool always_hide_play_pause_button_ = false; bool always_hide_play_pause_button_ = false;
// Current playback state on the video in Picture-in-Picture window. It is
// used to show/hide controls.
PlaybackState playback_state_ = kEndOfVideo;
// The upper and lower bounds of |current_size_|. These are determined by the // The upper and lower bounds of |current_size_|. These are determined by the
// size of the primary display work area when Picture-in-Picture is initiated. // size of the primary display work area when Picture-in-Picture is initiated.
// TODO(apacible): Update these bounds when the display the window is on // TODO(apacible): Update these bounds when the display the window is on
...@@ -194,7 +190,7 @@ class OverlayWindowViews : public content::OverlayWindow, ...@@ -194,7 +190,7 @@ class OverlayWindowViews : public content::OverlayWindow,
std::unique_ptr<views::View> controls_parent_view_; std::unique_ptr<views::View> controls_parent_view_;
std::unique_ptr<views::CloseImageButton> close_controls_view_; std::unique_ptr<views::CloseImageButton> close_controls_view_;
std::unique_ptr<views::ResizeHandleButton> resize_handle_view_; std::unique_ptr<views::ResizeHandleButton> resize_handle_view_;
std::unique_ptr<views::ToggleImageButton> play_pause_controls_view_; std::unique_ptr<views::PlaybackImageButton> play_pause_controls_view_;
std::unique_ptr<views::ControlImageButton> first_custom_controls_view_; std::unique_ptr<views::ControlImageButton> first_custom_controls_view_;
std::unique_ptr<views::ControlImageButton> second_custom_controls_view_; std::unique_ptr<views::ControlImageButton> second_custom_controls_view_;
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
...@@ -204,6 +200,10 @@ class OverlayWindowViews : public content::OverlayWindow, ...@@ -204,6 +200,10 @@ class OverlayWindowViews : public content::OverlayWindow,
// Automatically hides the controls a few seconds after user tap gesture. // Automatically hides the controls a few seconds after user tap gesture.
base::RetainingOneShotTimer hide_controls_timer_; base::RetainingOneShotTimer hide_controls_timer_;
// Current playback state on the video in Picture-in-Picture window. It is
// used to toggle play/pause/replay button.
PlaybackState playback_state_for_testing_ = kEndOfVideo;
DISALLOW_COPY_AND_ASSIGN(OverlayWindowViews); DISALLOW_COPY_AND_ASSIGN(OverlayWindowViews);
}; };
......
// 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.
#include "chrome/browser/ui/views/overlay/playback_image_button.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/grit/generated_resources.h"
#include "components/vector_icons/vector_icons.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/vector_icons.h"
namespace {
SkColor kBackgroundColor = SK_ColorWHITE;
SkColor kPlaybackIconColor = SK_ColorBLACK;
} // namespace
namespace views {
PlaybackImageButton::PlaybackImageButton(ButtonListener* listener)
: ImageButton(listener) {
SetImageAlignment(views::ImageButton::ALIGN_CENTER,
views::ImageButton::ALIGN_MIDDLE);
SetFocusForPlatform();
const base::string16 playback_accessible_button_label(
l10n_util::GetStringUTF16(
IDS_PICTURE_IN_PICTURE_PLAY_PAUSE_CONTROL_ACCESSIBLE_TEXT));
SetAccessibleName(playback_accessible_button_label);
SetInstallFocusRingOnFocus(true);
}
PlaybackImageButton::~PlaybackImageButton() = default;
void PlaybackImageButton::SetButtonSize(const gfx::Size& size) {
SetSize(size);
play_image_ = gfx::CreateVectorIcon(vector_icons::kPlayArrowIcon,
size.width() / 2, kPlaybackIconColor);
pause_image_ = gfx::CreateVectorIcon(vector_icons::kPauseIcon,
size.width() / 2, kPlaybackIconColor);
replay_image_ = gfx::CreateVectorIcon(vector_icons::kReplayIcon,
size.width() / 2, kPlaybackIconColor);
const gfx::ImageSkia background_image_ = gfx::CreateVectorIcon(
kPictureInPictureControlBackgroundIcon, size.width(), kBackgroundColor);
SetBackgroundImage(kBackgroundColor, &background_image_, &background_image_);
UpdateImageAndTooltipText();
}
void PlaybackImageButton::SetPlaybackState(
const OverlayWindowViews::PlaybackState playback_state) {
if (playback_state_ == playback_state)
return;
playback_state_ = playback_state;
UpdateImageAndTooltipText();
}
void PlaybackImageButton::UpdateImageAndTooltipText() {
switch (playback_state_) {
case OverlayWindowViews::kPlaying:
SetImage(views::Button::STATE_NORMAL, pause_image_);
SetTooltipText(
l10n_util::GetStringUTF16(IDS_PICTURE_IN_PICTURE_PAUSE_CONTROL_TEXT));
break;
case OverlayWindowViews::kPaused:
SetImage(views::Button::STATE_NORMAL, play_image_);
SetTooltipText(
l10n_util::GetStringUTF16(IDS_PICTURE_IN_PICTURE_PLAY_CONTROL_TEXT));
break;
case OverlayWindowViews::kEndOfVideo:
SetImage(views::Button::STATE_NORMAL, replay_image_);
SetTooltipText(l10n_util::GetStringUTF16(
IDS_PICTURE_IN_PICTURE_REPLAY_CONTROL_TEXT));
break;
}
}
} // namespace views
// 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.
#ifndef CHROME_BROWSER_UI_VIEWS_OVERLAY_PLAYBACK_IMAGE_BUTTON_H_
#define CHROME_BROWSER_UI_VIEWS_OVERLAY_PLAYBACK_IMAGE_BUTTON_H_
#include "chrome/browser/ui/views/overlay/overlay_window_views.h"
#include "ui/views/controls/button/image_button.h"
namespace views {
// A resizable playback button with 3 states: play/pause/replay.
class PlaybackImageButton : public views::ImageButton {
public:
explicit PlaybackImageButton(ButtonListener* listener);
~PlaybackImageButton() override;
// Set button size and make sure images are sized accordindly.
void SetButtonSize(const gfx::Size& size);
// Show appropriate images based on playback state.
void SetPlaybackState(const OverlayWindowViews::PlaybackState playback_state);
private:
void UpdateImageAndTooltipText();
OverlayWindowViews::PlaybackState playback_state_;
gfx::ImageSkia play_image_;
gfx::ImageSkia pause_image_;
gfx::ImageSkia replay_image_;
DISALLOW_COPY_AND_ASSIGN(PlaybackImageButton);
};
} // namespace views
#endif // CHROME_BROWSER_UI_VIEWS_OVERLAY_PLAYBACK_IMAGE_BUTTON_H_
...@@ -43,6 +43,7 @@ aggregate_vector_icons("components_vector_icons") { ...@@ -43,6 +43,7 @@ aggregate_vector_icons("components_vector_icons") {
"play_arrow.icon", "play_arrow.icon",
"protocol_handler.icon", "protocol_handler.icon",
"reload.icon", "reload.icon",
"replay.icon",
"screen_share.icon", "screen_share.icon",
"search.icon", "search.icon",
"usb.icon", "usb.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, 12, 5,
V_LINE_TO, 1,
LINE_TO, 7, 6,
R_LINE_TO, 5, 5,
V_LINE_TO, 7,
R_CUBIC_TO, 3.31f, 0, 6, 2.69f, 6, 6,
R_CUBIC_TO, 0, 3.31f, -2.69f, 6, -6, 6,
R_CUBIC_TO, -3.31f, 0, -6, -2.69f, -6, -6,
H_LINE_TO, 4,
R_CUBIC_TO, 0, 4.42f, 3.58f, 8, 8, 8,
R_CUBIC_TO, 4.42f, 0, 8, -3.58f, 8, -8,
R_CUBIC_TO, 0, -4.42f, -3.58f, -8, -8, -8,
CLOSE
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