|
@@ -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<DevicePixelPoint> 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<int>(), 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);
|
|
|
+ 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);
|
|
|
+}
|
|
|
|
|
|
- context.painter().fill_rect(control_box_rect.to_type<int>(), control_box_color.with_alpha(0xd0));
|
|
|
- media_element.cached_layout_boxes({}).control_box_rect = context.scale_to_css_rect(control_box_rect);
|
|
|
+MediaPaintable::Components MediaPaintable::compute_control_bar_components(PaintContext& context, HTML::HTMLMediaElement const& media_element, DevicePixelRect media_rect) const
|
|
|
+{
|
|
|
+ auto maximum_control_box_height = context.rounded_device_pixels(40);
|
|
|
+ auto component_padding = context.rounded_device_pixels(5);
|
|
|
|
|
|
- control_box_rect = paint_control_bar_playback_button(context, media_element, control_box_rect, mouse_position);
|
|
|
- control_box_rect.take_from_left(playback_padding);
|
|
|
+ Components components {};
|
|
|
|
|
|
- control_box_rect = paint_control_bar_timeline(context, media_element, control_box_rect, mouse_position);
|
|
|
- control_box_rect.take_from_left(playback_padding);
|
|
|
+ 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);
|
|
|
|
|
|
- control_box_rect = paint_control_bar_timestamp(context, media_element, control_box_rect);
|
|
|
- control_box_rect.take_from_left(playback_padding);
|
|
|
-}
|
|
|
+ auto remaining_rect = components.control_box_rect;
|
|
|
+ remaining_rect.shrink(component_padding * 2, 0);
|
|
|
|
|
|
-DevicePixelRect MediaPaintable::paint_control_bar_playback_button(PaintContext& context, HTML::HTMLMediaElement const& media_element, DevicePixelRect control_box_rect, Optional<DevicePixelPoint> 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_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 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;
|
|
|
+ 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 playback_button_location = control_box_rect.top_left().translated(playback_button_offset_x, playback_button_offset_y);
|
|
|
+ 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<DevicePixels::Type>(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);
|
|
|
+ }
|
|
|
|
|
|
- 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);
|
|
|
+ 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<DevicePixelPoint> 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() && playback_button_hover_rect.contains(*mouse_position);
|
|
|
+ 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<int>(), playback_button_color);
|
|
|
context.painter().fill_rect(pause_button_right_rect.to_type<int>(), 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<DevicePixelPoint> const& mouse_position) const
|
|
|
+void MediaPaintable::paint_control_bar_timeline(PaintContext& context, HTML::HTMLMediaElement const& media_element, Components const& components, Optional<DevicePixelPoint> 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<HTML::HTMLAudioElement>(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<double>(static_cast<int>(timeline_rect.width())) * playback_percentage;
|
|
|
-
|
|
|
- auto timeline_button_size = min(maximum_timeline_button_size, timeline_rect.height() / 2);
|
|
|
+ auto playback_position = static_cast<double>(static_cast<int>(timelime_scrub_rect.width())) * playback_percentage;
|
|
|
auto timeline_button_offset_x = static_cast<DevicePixels>(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<int>(), 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<int>(), 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<int>(), 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()));
|
|
|
-
|
|
|
- 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<DevicePixels::Type>(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<int>(), timestamp, *font, Gfx::TextAlignment::CenterLeft, Color::White);
|
|
|
+ if (components.timestamp_rect.is_empty())
|
|
|
+ return;
|
|
|
|
|
|
- control_box_rect.take_from_left(timestamp_rect.width());
|
|
|
- return control_box_rect;
|
|
|
+ context.painter().draw_text(components.timestamp_rect.to_type<int>(), components.timestamp, *components.timestamp_font, Gfx::TextAlignment::CenterLeft, Color::White);
|
|
|
}
|
|
|
|
|
|
MediaPaintable::DispatchEventOfSameName MediaPaintable::handle_mouseup(Badge<EventHandler>, CSSPixelPoint position, unsigned button, unsigned)
|