diff --git a/Userland/Libraries/LibWeb/Painting/MediaPaintable.cpp b/Userland/Libraries/LibWeb/Painting/MediaPaintable.cpp index a3c99133c8..d840a6d9c7 100644 --- a/Userland/Libraries/LibWeb/Painting/MediaPaintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/MediaPaintable.cpp @@ -55,44 +55,69 @@ void MediaPaintable::fill_triangle(Gfx::Painter& painter, Gfx::IntPoint location void MediaPaintable::paint_media_controls(PaintContext& context, HTML::HTMLMediaElement const& media_element, DevicePixelRect media_rect, Optional const& mouse_position) const { - auto maximum_control_box_size = context.rounded_device_pixels(40); - auto playback_padding = context.rounded_device_pixels(5); + auto components = compute_control_bar_components(context, media_element, media_rect); + context.painter().fill_rect(components.control_box_rect.to_type(), control_box_color.with_alpha(0xd0)); - auto control_box_rect = media_rect; - if (control_box_rect.height() > maximum_control_box_size) - control_box_rect.take_from_top(control_box_rect.height() - maximum_control_box_size); - - context.painter().fill_rect(control_box_rect.to_type(), control_box_color.with_alpha(0xd0)); - media_element.cached_layout_boxes({}).control_box_rect = context.scale_to_css_rect(control_box_rect); - - control_box_rect = paint_control_bar_playback_button(context, media_element, control_box_rect, mouse_position); - control_box_rect.take_from_left(playback_padding); - - control_box_rect = paint_control_bar_timeline(context, media_element, control_box_rect, mouse_position); - control_box_rect.take_from_left(playback_padding); - - control_box_rect = paint_control_bar_timestamp(context, media_element, control_box_rect); - control_box_rect.take_from_left(playback_padding); + paint_control_bar_playback_button(context, media_element, components, mouse_position); + paint_control_bar_timeline(context, media_element, components, mouse_position); + paint_control_bar_timestamp(context, components); } -DevicePixelRect MediaPaintable::paint_control_bar_playback_button(PaintContext& context, HTML::HTMLMediaElement const& media_element, DevicePixelRect control_box_rect, Optional const& mouse_position) const +MediaPaintable::Components MediaPaintable::compute_control_bar_components(PaintContext& context, HTML::HTMLMediaElement const& media_element, DevicePixelRect media_rect) const { - auto maximum_playback_button_size = context.rounded_device_pixels(15); - auto maximum_playback_button_offset_x = context.rounded_device_pixels(15); + auto maximum_control_box_height = context.rounded_device_pixels(40); + auto component_padding = context.rounded_device_pixels(5); - 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; + Components components {}; - auto playback_button_location = control_box_rect.top_left().translated(playback_button_offset_x, playback_button_offset_y); + components.control_box_rect = media_rect; + if (components.control_box_rect.height() > maximum_control_box_height) + components.control_box_rect.take_from_top(components.control_box_rect.height() - maximum_control_box_height); - auto playback_button_hover_rect = DevicePixelRect { - control_box_rect.top_left(), - { playback_button_size + playback_button_offset_x * 2, control_box_rect.height() } - }; - media_element.cached_layout_boxes({}).playback_button_rect = context.scale_to_css_rect(playback_button_hover_rect); + auto remaining_rect = components.control_box_rect; + remaining_rect.shrink(component_padding * 2, 0); - auto playback_button_is_hovered = mouse_position.has_value() && playback_button_hover_rect.contains(*mouse_position); + auto playback_button_rect_width = min(context.rounded_device_pixels(40), remaining_rect.width()); + components.playback_button_rect = remaining_rect; + components.playback_button_rect.set_width(playback_button_rect_width); + remaining_rect.take_from_left(playback_button_rect_width); + + auto current_time = human_readable_digital_time(round(media_element.current_time())); + auto duration = human_readable_digital_time(isnan(media_element.duration()) ? 0 : round(media_element.duration())); + components.timestamp = String::formatted("{} / {}", current_time, duration).release_value_but_fixme_should_propagate_errors(); + + auto const& scaled_font = layout_node().scaled_font(context); + components.timestamp_font = scaled_font.with_size(10); + if (!components.timestamp_font) + components.timestamp_font = scaled_font; + + auto timestamp_size = DevicePixels { static_cast(ceilf(components.timestamp_font->width(components.timestamp))) }; + if (timestamp_size <= remaining_rect.width()) { + components.timestamp_rect = remaining_rect; + components.timestamp_rect.take_from_left(remaining_rect.width() - timestamp_size); + remaining_rect.take_from_right(timestamp_size + component_padding); + } + + components.timeline_button_size = context.rounded_device_pixels(16); + if ((components.timeline_button_size * 3) <= remaining_rect.width()) + components.timeline_rect = remaining_rect; + + media_element.cached_layout_boxes({}).control_box_rect = context.scale_to_css_rect(components.control_box_rect); + media_element.cached_layout_boxes({}).playback_button_rect = context.scale_to_css_rect(components.playback_button_rect); + media_element.cached_layout_boxes({}).timeline_rect = context.scale_to_css_rect(components.timeline_rect); + + return components; +} + +void MediaPaintable::paint_control_bar_playback_button(PaintContext& context, HTML::HTMLMediaElement const& media_element, Components const& components, Optional const& mouse_position) +{ + auto playback_button_size = components.playback_button_rect.width() * 4 / 10; + + auto playback_button_offset_x = (components.playback_button_rect.width() - playback_button_size) / 2; + auto playback_button_offset_y = (components.playback_button_rect.height() - playback_button_size) / 2; + auto playback_button_location = components.playback_button_rect.top_left().translated(playback_button_offset_x, playback_button_offset_y); + + auto playback_button_is_hovered = mouse_position.has_value() && components.playback_button_rect.contains(*mouse_position); auto playback_button_color = control_button_color(playback_button_is_hovered); if (media_element.paused()) { @@ -106,85 +131,55 @@ DevicePixelRect MediaPaintable::paint_control_bar_playback_button(PaintContext& } else { DevicePixelRect pause_button_left_rect { playback_button_location, - { maximum_playback_button_size / 3, playback_button_size } + { playback_button_size / 3, playback_button_size } }; DevicePixelRect pause_button_right_rect { - playback_button_location.translated(maximum_playback_button_size * 2 / 3, 0), - { maximum_playback_button_size / 3, playback_button_size } + playback_button_location.translated(playback_button_size * 2 / 3, 0), + { playback_button_size / 3, playback_button_size } }; 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 MediaPaintable::paint_control_bar_timeline(PaintContext& context, HTML::HTMLMediaElement const& media_element, DevicePixelRect control_box_rect, Optional const& mouse_position) const +void MediaPaintable::paint_control_bar_timeline(PaintContext& context, HTML::HTMLMediaElement const& media_element, Components const& components, Optional const& mouse_position) { - auto maximum_timeline_button_size = context.rounded_device_pixels(16); + if (components.timeline_rect.is_empty()) + return; - auto timeline_rect = control_box_rect; - if (is(media_element)) - timeline_rect.set_width(min(control_box_rect.width() * 6 / 10, timeline_rect.width() * 4 / 10)); - else - timeline_rect.set_width(min(control_box_rect.width() * 6 / 10, timeline_rect.width())); - media_element.cached_layout_boxes({}).timeline_rect = context.scale_to_css_rect(timeline_rect); + auto timelime_scrub_rect = components.timeline_rect; + timelime_scrub_rect.shrink(components.timeline_button_size, timelime_scrub_rect.height() - components.timeline_button_size / 2); auto playback_percentage = isnan(media_element.duration()) ? 0.0 : media_element.current_time() / media_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 playback_position = static_cast(static_cast(timelime_scrub_rect.width())) * playback_percentage; 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; + auto timeline_past_rect = 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; + auto timeline_future_rect = 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_button_rect = timelime_scrub_rect; + timeline_button_rect.shrink(timelime_scrub_rect.width() - components.timeline_button_size, timelime_scrub_rect.height() - components.timeline_button_size); + timeline_button_rect.set_x(timelime_scrub_rect.x() + timeline_button_offset_x - components.timeline_button_size / 2); - auto timeline_is_hovered = mouse_position.has_value() && timeline_rect.contains(*mouse_position); + auto timeline_is_hovered = mouse_position.has_value() && components.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 MediaPaintable::paint_control_bar_timestamp(PaintContext& context, HTML::HTMLMediaElement const& media_element, DevicePixelRect control_box_rect) const +void MediaPaintable::paint_control_bar_timestamp(PaintContext& context, Components const& components) { - auto current_time = human_readable_digital_time(round(media_element.current_time())); - auto duration = human_readable_digital_time(isnan(media_element.duration()) ? 0 : round(media_element.duration())); + if (components.timestamp_rect.is_empty()) + return; - auto timestamp = String::formatted("{} / {}", current_time, duration).release_value_but_fixme_should_propagate_errors(); - - auto const& scaled_font = layout_node().scaled_font(context); - auto font = scaled_font.with_size(10); - if (!font) - font = scaled_font; - - auto timestamp_size = static_cast(ceilf(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, *font, Gfx::TextAlignment::CenterLeft, Color::White); - - control_box_rect.take_from_left(timestamp_rect.width()); - return control_box_rect; + context.painter().draw_text(components.timestamp_rect.to_type(), components.timestamp, *components.timestamp_font, Gfx::TextAlignment::CenterLeft, Color::White); } MediaPaintable::DispatchEventOfSameName MediaPaintable::handle_mouseup(Badge, CSSPixelPoint position, unsigned button, unsigned) diff --git a/Userland/Libraries/LibWeb/Painting/MediaPaintable.h b/Userland/Libraries/LibWeb/Painting/MediaPaintable.h index fcdcd67c29..96d2309b31 100644 --- a/Userland/Libraries/LibWeb/Painting/MediaPaintable.h +++ b/Userland/Libraries/LibWeb/Painting/MediaPaintable.h @@ -8,6 +8,7 @@ #include #include +#include namespace Web::Painting { @@ -23,13 +24,26 @@ protected: void paint_media_controls(PaintContext&, HTML::HTMLMediaElement const&, DevicePixelRect media_rect, Optional const& mouse_position) const; private: + struct Components { + DevicePixelRect control_box_rect; + DevicePixelRect playback_button_rect; + + DevicePixelRect timeline_rect; + DevicePixels timeline_button_size; + + String timestamp; + RefPtr timestamp_font; + DevicePixelRect timestamp_rect; + }; + virtual bool wants_mouse_events() const override { return true; } virtual DispatchEventOfSameName handle_mouseup(Badge, CSSPixelPoint, unsigned button, unsigned modifiers) override; virtual DispatchEventOfSameName handle_mousemove(Badge, CSSPixelPoint, unsigned buttons, unsigned modifiers) override; - DevicePixelRect paint_control_bar_playback_button(PaintContext&, HTML::HTMLMediaElement const&, DevicePixelRect control_box_rect, Optional const& mouse_position) const; - DevicePixelRect paint_control_bar_timeline(PaintContext&, HTML::HTMLMediaElement const&, DevicePixelRect control_box_rect, Optional const& mouse_position) const; - DevicePixelRect paint_control_bar_timestamp(PaintContext&, HTML::HTMLMediaElement const&, DevicePixelRect control_box_rect) const; + Components compute_control_bar_components(PaintContext&, HTML::HTMLMediaElement const&, DevicePixelRect media_rect) const; + static void paint_control_bar_playback_button(PaintContext&, HTML::HTMLMediaElement const&, Components const&, Optional const& mouse_position); + static void paint_control_bar_timeline(PaintContext&, HTML::HTMLMediaElement const&, Components const&, Optional const& mouse_position); + static void paint_control_bar_timestamp(PaintContext&, Components const&); }; }