From fe664903993809b8ecc2318b31186da6e6ece82e Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Mon, 10 Apr 2023 11:36:18 -0400 Subject: [PATCH] LibWeb: Paint a media timeline on HTMLVideoElement layout nodes --- .../LibWeb/Painting/VideoPaintable.cpp | 82 ++++++++++++++++++- .../LibWeb/Painting/VideoPaintable.h | 4 + 2 files changed, 83 insertions(+), 3 deletions(-) diff --git a/Userland/Libraries/LibWeb/Painting/VideoPaintable.cpp b/Userland/Libraries/LibWeb/Painting/VideoPaintable.cpp index 4b2c3e2058..21017e89db 100644 --- a/Userland/Libraries/LibWeb/Painting/VideoPaintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/VideoPaintable.cpp @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -62,6 +63,8 @@ void VideoPaintable::paint(PaintContext& context, PaintPhase phase) const return; auto video_rect = context.rounded_device_rect(absolute_rect()); + context.painter().add_clip_rect(video_rect.to_type()); + ScopedCornerRadiusClip corner_clip { context, context.painter(), video_rect, normalized_border_radii_data(ShrinkRadiiForBorders::Yes) }; auto const& video_element = layout_box().dom_node(); @@ -90,8 +93,7 @@ void VideoPaintable::paint(PaintContext& context, PaintPhase phase) const void VideoPaintable::paint_loaded_video_controls(PaintContext& context, HTML::HTMLVideoElement const& video_element, DevicePixelRect video_rect, Optional const& mouse_position) const { auto maximum_control_box_size = context.rounded_device_pixels(30); - auto maximum_playback_button_size = context.rounded_device_pixels(15); - auto maximum_playback_button_offset_x = context.rounded_device_pixels(15); + auto playback_padding = context.rounded_device_pixels(5); auto is_hovered = document().hovered_node() == &video_element; auto is_paused = video_element.paused(); @@ -105,6 +107,21 @@ void VideoPaintable::paint_loaded_video_controls(PaintContext& context, HTML::HT context.painter().fill_rect(control_box_rect.to_type(), control_box_color.with_alpha(0xd0)); + control_box_rect = paint_control_bar_playback_button(context, video_element, control_box_rect, mouse_position); + control_box_rect.take_from_left(playback_padding); + + control_box_rect = paint_control_bar_timeline(context, video_element, control_box_rect, mouse_position); + control_box_rect.take_from_left(playback_padding); + + control_box_rect = paint_control_bar_timestamp(context, video_element, control_box_rect); + control_box_rect.take_from_left(playback_padding); +} + +DevicePixelRect VideoPaintable::paint_control_bar_playback_button(PaintContext& context, HTML::HTMLVideoElement const& video_element, DevicePixelRect control_box_rect, Optional const& mouse_position) const +{ + auto maximum_playback_button_size = context.rounded_device_pixels(15); + auto maximum_playback_button_offset_x = context.rounded_device_pixels(15); + auto playback_button_size = min(maximum_playback_button_size, control_box_rect.height() / 2); auto playback_button_offset_x = min(maximum_playback_button_offset_x, control_box_rect.width()); auto playback_button_offset_y = (control_box_rect.height() - playback_button_size) / 2; @@ -119,7 +136,7 @@ void VideoPaintable::paint_loaded_video_controls(PaintContext& context, HTML::HT auto playback_button_is_hovered = mouse_position.has_value() && playback_button_hover_rect.contains(*mouse_position); auto playback_button_color = control_button_color(playback_button_is_hovered); - if (is_paused) { + if (video_element.paused()) { Array play_button_coordinates { { { 0, 0 }, { static_cast(playback_button_size), static_cast(playback_button_size) / 2 }, @@ -140,6 +157,65 @@ void VideoPaintable::paint_loaded_video_controls(PaintContext& context, HTML::HT context.painter().fill_rect(pause_button_left_rect.to_type(), playback_button_color); context.painter().fill_rect(pause_button_right_rect.to_type(), playback_button_color); } + + control_box_rect.take_from_left(playback_button_hover_rect.width()); + return control_box_rect; +} + +DevicePixelRect VideoPaintable::paint_control_bar_timeline(PaintContext& context, HTML::HTMLVideoElement const& video_element, DevicePixelRect control_box_rect, Optional const& mouse_position) const +{ + auto maximum_timeline_button_size = context.rounded_device_pixels(16); + + auto timeline_rect = control_box_rect; + timeline_rect.set_width(min(control_box_rect.width() * 6 / 10, timeline_rect.width())); + + auto playback_percentage = video_element.current_time() / video_element.duration(); + auto playback_position = static_cast(static_cast(timeline_rect.width())) * playback_percentage; + + auto timeline_button_size = min(maximum_timeline_button_size, timeline_rect.height() / 2); + auto timeline_button_offset_x = static_cast(round(playback_position)); + + Gfx::AntiAliasingPainter painter { context.painter() }; + + auto playback_timelime_scrub_rect = timeline_rect; + playback_timelime_scrub_rect.shrink(0, timeline_rect.height() - timeline_button_size / 2); + + auto timeline_past_rect = playback_timelime_scrub_rect; + timeline_past_rect.set_width(timeline_button_offset_x); + painter.fill_rect_with_rounded_corners(timeline_past_rect.to_type(), control_highlight_color.lightened(), 4); + + auto timeline_future_rect = playback_timelime_scrub_rect; + timeline_future_rect.take_from_left(timeline_button_offset_x); + painter.fill_rect_with_rounded_corners(timeline_future_rect.to_type(), Color::Black, 4); + + auto timeline_button_rect = timeline_rect; + timeline_button_rect.shrink(timeline_rect.width() - timeline_button_size, timeline_rect.height() - timeline_button_size); + timeline_button_rect.set_x(timeline_rect.x() + timeline_button_offset_x - timeline_button_size / 2); + + auto timeline_is_hovered = mouse_position.has_value() && timeline_rect.contains(*mouse_position); + auto timeline_color = control_button_color(timeline_is_hovered); + painter.fill_ellipse(timeline_button_rect.to_type(), timeline_color); + + control_box_rect.take_from_left(timeline_rect.width() + timeline_button_size / 2); + return control_box_rect; +} + +DevicePixelRect VideoPaintable::paint_control_bar_timestamp(PaintContext& context, HTML::HTMLVideoElement const& video_element, DevicePixelRect control_box_rect) const +{ + auto current_time = human_readable_digital_time(round(video_element.current_time())); + auto duration = human_readable_digital_time(round(video_element.duration())); + auto timestamp = String::formatted("{} / {}", current_time, duration).release_value_but_fixme_should_propagate_errors(); + + auto timestamp_size = static_cast(ceilf(context.painter().font().width(timestamp))); + if (timestamp_size > control_box_rect.width()) + return control_box_rect; + + auto timestamp_rect = control_box_rect; + timestamp_rect.set_width(timestamp_size); + context.painter().draw_text(timestamp_rect.to_type(), timestamp, Gfx::TextAlignment::CenterLeft, Color::White); + + control_box_rect.take_from_left(timestamp_rect.width()); + return control_box_rect; } void VideoPaintable::paint_placeholder_video_controls(PaintContext& context, DevicePixelRect video_rect, Optional const& mouse_position) const diff --git a/Userland/Libraries/LibWeb/Painting/VideoPaintable.h b/Userland/Libraries/LibWeb/Painting/VideoPaintable.h index ac4e1fa09e..f4b5dea302 100644 --- a/Userland/Libraries/LibWeb/Painting/VideoPaintable.h +++ b/Userland/Libraries/LibWeb/Painting/VideoPaintable.h @@ -30,6 +30,10 @@ private: virtual DispatchEventOfSameName handle_mousemove(Badge, CSSPixelPoint, unsigned buttons, unsigned modifiers) override; void paint_loaded_video_controls(PaintContext&, HTML::HTMLVideoElement const&, DevicePixelRect video_rect, Optional const& mouse_position) const; + DevicePixelRect paint_control_bar_playback_button(PaintContext&, HTML::HTMLVideoElement const&, DevicePixelRect control_box_rect, Optional const& mouse_position) const; + DevicePixelRect paint_control_bar_timeline(PaintContext&, HTML::HTMLVideoElement const&, DevicePixelRect control_box_rect, Optional const& mouse_position) const; + DevicePixelRect paint_control_bar_timestamp(PaintContext&, HTML::HTMLVideoElement const&, DevicePixelRect control_box_rect) const; + void paint_placeholder_video_controls(PaintContext&, DevicePixelRect video_rect, Optional const& mouse_position) const; };