Skip to content

Commit

Permalink
Enhance seeker on PIP window (#21619)
Browse files Browse the repository at this point in the history
* Enhance seeker on PIP window

* Hide invalid timestamp
* Hide seeker when mouse is not over the window
* Enable seeking when the MediaSession is controllable
* When seeker is overlapped with bottom resizable area, treat mouse
  events for seeker.
* On left or right key pressed, seek back or forward 10 seconds.
  • Loading branch information
sangwoo108 committed Jan 22, 2024
1 parent 1bf2d52 commit 5a81c91
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 18 deletions.
84 changes: 72 additions & 12 deletions browser/ui/views/overlay/brave_video_overlay_window_views.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "chrome/browser/ui/views/overlay/simple_overlay_window_image_button.h"
#include "chrome/browser/ui/views/overlay/toggle_camera_button.h"
#include "chrome/browser/ui/views/overlay/toggle_microphone_button.h"
#include "ui/base/hit_test.h"
#include "ui/gfx/canvas.h"

namespace {
Expand All @@ -46,7 +47,9 @@ std::u16string ToString(const media_session::MediaPosition& position) {
ToString(position.duration())});
}

class Seeker : public views::Slider, public views::SliderListener {
class Seeker : public views::Slider,
public views::SliderListener,
public views::ViewTargeterDelegate {
public:
METADATA_HEADER(Seeker);

Expand All @@ -63,6 +66,8 @@ class Seeker : public views::Slider, public views::SliderListener {
SetRenderingStyle(RenderingStyle::kMinimalStyle);
SetPreferredSize(gfx::Size(kPreferredHeight, kPreferredHeight));
thumb_animation_.SetSlideDuration(base::Milliseconds(150));

SetEventTargeter(std::make_unique<views::ViewTargeter>(this));
}
~Seeker() override = default;

Expand Down Expand Up @@ -145,8 +150,28 @@ class Seeker : public views::Slider, public views::SliderListener {
listener_->SliderDragEnded(sender);
}

// views::ViewTargeterDelegate:
bool DoesIntersectRect(const views::View* target,
const gfx::Rect& rect) const override {
if (!GetEnabled() || !IsDrawn()) {
return false;
}

// Exclude resize area for the window from hit test.
// Note that we're using half of the width of resize area specified in
// video_overlay_window_views.cc for corners.
constexpr int kResizeAreaWidth = 8;
auto seeker_bounds = GetLocalBounds();
seeker_bounds.Inset(gfx::Insets::TLBR(0, kResizeAreaWidth,
(kPreferredHeight - kLineHeight) / 2,
kResizeAreaWidth));
return target == this && rect.Intersects(seeker_bounds);
}

private:
bool ShouldShowThumb() const { return dragging_ || IsMouseHovered(); }
bool ShouldShowThumb() const {
return GetEnabled() && (dragging_ || IsMouseHovered());
}

raw_ptr<views::SliderListener> listener_ = nullptr;

Expand Down Expand Up @@ -180,14 +205,12 @@ void BraveVideoOverlayWindowViews::SetUpViews() {
timestamp_->layer()->SetFillsBoundsOpaquely(false);
timestamp_->layer()->SetName("Timestamp");

// Unlike other controls, seeker should be visible even when mouse is not
// hovered on this window. So seeker is attached to the root view directly.
auto seeker = std::make_unique<Seeker>(this);
seeker_ = seeker.get();
seeker_ =
controls_container_view_->AddChildView(std::make_unique<Seeker>(this));

// views in |view_holder_| will be added as child of contents view in
// VideoOverlayWindowViews::OnRootViewReady().
view_holder_.push_back(std::move(seeker));
// Before we get the media position, we should hide timestamp and seeker.
timestamp_->SetVisible(false);
seeker_->SetVisible(false);
}

void BraveVideoOverlayWindowViews::OnUpdateControlsBounds() {
Expand Down Expand Up @@ -316,7 +339,9 @@ void BraveVideoOverlayWindowViews::SetPlaybackState(

bool BraveVideoOverlayWindowViews::ControlsHitTestContainsPoint(
const gfx::Point& point) {
if (seeker_->GetMirroredBounds().Contains(point)) {
gfx::Point point_in_seeker = views::View::ConvertPointToTarget(
seeker_->parent(), seeker_.get(), point);
if (seeker_->HitTestPoint(point_in_seeker)) {
return true;
}

Expand All @@ -331,6 +356,7 @@ void BraveVideoOverlayWindowViews::ShowInactive() {
VideoOverlayWindowViews::ShowInactive();
UpdateTimestampPeriodically();
}

void BraveVideoOverlayWindowViews::Close() {
timestamp_update_timer_.Stop();
VideoOverlayWindowViews::Close();
Expand All @@ -341,6 +367,35 @@ void BraveVideoOverlayWindowViews::Hide() {
VideoOverlayWindowViews::Hide();
}

int BraveVideoOverlayWindowViews::GetNonClientComponent(
const gfx::Point& point) {
if (seeker_ && seeker_->GetWidget() == this) {
gfx::Point point_in_seeker(point);
views::View::ConvertPointFromWidget(seeker_.get(), &point_in_seeker);
if (seeker_->HitTestPoint(point_in_seeker)) {
// We want to handel mouse event on seeker when it's visible, rather than
// consider it as non-client area
return HTCLIENT;
}
}

return VideoOverlayWindowViews::GetNonClientComponent(point);
}

void BraveVideoOverlayWindowViews::OnKeyEvent(ui::KeyEvent* event) {
if (event->type() == ui::ET_KEY_PRESSED && media_position_) {
if (event->key_code() == ui::VKEY_LEFT) {
controller_->SeekTo(media_position_->GetPosition() - base::Seconds(10));
} else if (event->key_code() == ui::VKEY_RIGHT) {
controller_->SeekTo(media_position_->GetPosition() + base::Seconds(10));
}
event->SetHandled();
return;
}

VideoOverlayWindowViews::OnKeyEvent(event);
}

void BraveVideoOverlayWindowViews::SliderValueChanged(
views::Slider* sender,
float value,
Expand Down Expand Up @@ -392,7 +447,13 @@ void BraveVideoOverlayWindowViews::UpdateTimestampPosition() {

void BraveVideoOverlayWindowViews::UpdateTimestampPeriodically() {
// Update timestamp related UI controls
if (media_position_) {

// We don't need to show seeker and timestamp when the duration is less than
// a second, infinite or zero.
if (media_position_ &&
(!media_position_->duration().is_inf() &&
!media_position_->duration().is_zero() &&
static_cast<int>(media_position_->duration().InSecondsF()) > 0)) {
auto new_time = ToString(*media_position_);
if (new_time != timestamp_->GetText()) {
timestamp_->SetText(new_time);
Expand All @@ -407,7 +468,6 @@ void BraveVideoOverlayWindowViews::UpdateTimestampPeriodically() {
if (!seeker_->GetVisible()) {
seeker_->SetVisible(true);
}

} else {
timestamp_->SetText({});
seeker_->SetValue(0);
Expand Down
2 changes: 2 additions & 0 deletions browser/ui/views/overlay/brave_video_overlay_window_views.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class BraveVideoOverlayWindowViews : public VideoOverlayWindowViews,
void ShowInactive() override;
void Close() override;
void Hide() override;
int GetNonClientComponent(const gfx::Point& point) override;
void OnKeyEvent(ui::KeyEvent* event) override;

// views::SliderListener:
void SliderValueChanged(views::Slider* sender,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,16 @@
}

// Update seeker enabled state whenever actions are updated.
#define SetSkipAdButtonVisibility(SKIP_BUTTON_VISIBILITY) \
SetSkipAdButtonVisibility(SKIP_BUTTON_VISIBILITY); \
media_session_action_seek_to_handled_ = \
MediaSessionImpl::Get(web_contents()) \
->ShouldRouteAction( \
media_session::mojom::MediaSessionAction::kSeekTo); \
// Note that we allow seeking when media session is controllable referring to
// upstream code.
// https://source.chromium.org/chromium/chromium/src/+/main:content/browser/media/session/media_session_impl.cc;l=1687;drc=c686e8f4fd379312469fe018f5c390e9c8f20d0d
#define SetSkipAdButtonVisibility(SKIP_BUTTON_VISIBILITY) \
SetSkipAdButtonVisibility(SKIP_BUTTON_VISIBILITY); \
auto* session = MediaSessionImpl::Get(web_contents()); \
media_session_action_seek_to_handled_ = \
session->ShouldRouteAction( \
media_session::mojom::MediaSessionAction::kSeekTo) || \
session->IsControllable(); \
window_->SetSeekerEnabled(media_session_action_seek_to_handled_)

#include "src/content/browser/picture_in_picture/video_picture_in_picture_window_controller_impl.cc"
Expand Down

0 comments on commit 5a81c91

Please sign in to comment.