LibWeb: Draw a speaker on media elements to toggle muting audio

This commit is contained in:
Timothy Flynn 2023-06-15 15:09:47 -04:00 committed by Andreas Kling
parent 1107cb58c0
commit a4cb3b5d4d
Notes: sideshowbarker 2024-07-17 10:31:19 +09:00
4 changed files with 68 additions and 0 deletions

View File

@ -389,6 +389,9 @@ void HTMLMediaElement::volume_or_muted_attribute_changed()
// FIXME: Then, if the media element is not allowed to play, the user agent must run the internal pause steps for the media element.
if (auto* layout_node = this->layout_node())
layout_node->set_needs_display();
on_volume_change();
}

View File

@ -103,6 +103,7 @@ public:
Optional<CSSPixelRect> control_box_rect;
Optional<CSSPixelRect> playback_button_rect;
Optional<CSSPixelRect> timeline_rect;
Optional<CSSPixelRect> speaker_button_rect;
};
CachedLayoutBoxes& cached_layout_boxes(Badge<Painting::MediaPaintable>) const { return m_layout_boxes; }

View File

@ -61,6 +61,7 @@ void MediaPaintable::paint_media_controls(PaintContext& context, HTML::HTMLMedia
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);
paint_control_bar_speaker(context, media_element, components, mouse_position);
}
MediaPaintable::Components MediaPaintable::compute_control_bar_components(PaintContext& context, HTML::HTMLMediaElement const& media_element, DevicePixelRect media_rect) const
@ -82,6 +83,13 @@ MediaPaintable::Components MediaPaintable::compute_control_bar_components(PaintC
components.playback_button_rect.set_width(playback_button_rect_width);
remaining_rect.take_from_left(playback_button_rect_width);
components.speaker_button_size = context.rounded_device_pixels(30);
if (components.speaker_button_size <= remaining_rect.width()) {
components.speaker_button_rect = remaining_rect;
components.speaker_button_rect.take_from_left(remaining_rect.width() - components.speaker_button_size);
remaining_rect.take_from_right(components.speaker_button_size + component_padding);
}
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();
@ -105,6 +113,7 @@ MediaPaintable::Components MediaPaintable::compute_control_bar_components(PaintC
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);
media_element.cached_layout_boxes({}).speaker_button_rect = context.scale_to_css_rect(components.speaker_button_rect);
return components;
}
@ -182,6 +191,52 @@ void MediaPaintable::paint_control_bar_timestamp(PaintContext& context, Componen
context.painter().draw_text(components.timestamp_rect.to_type<int>(), components.timestamp, *components.timestamp_font, Gfx::TextAlignment::CenterLeft, Color::White);
}
void MediaPaintable::paint_control_bar_speaker(PaintContext& context, HTML::HTMLMediaElement const& media_element, Components const& components, Optional<DevicePixelPoint> const& mouse_position)
{
if (components.speaker_button_rect.is_empty())
return;
auto speaker_button_width = context.rounded_device_pixels(20);
auto speaker_button_height = context.rounded_device_pixels(15);
auto speaker_button_offset_x = (components.speaker_button_rect.width() - speaker_button_width) / 2;
auto speaker_button_offset_y = (components.speaker_button_rect.height() - speaker_button_height) / 2;
auto speaker_button_location = components.speaker_button_rect.top_left().translated(speaker_button_offset_x, speaker_button_offset_y);
auto device_point = [&](double x, double y) {
auto position = context.rounded_device_point({ x, y }) + speaker_button_location;
return position.to_type<DevicePixels::Type>().to_type<float>();
};
auto speaker_button_is_hovered = mouse_position.has_value() && components.speaker_button_rect.contains(*mouse_position);
auto speaker_button_color = control_button_color(speaker_button_is_hovered);
Gfx::AntiAliasingPainter painter { context.painter() };
Gfx::Path path;
path.move_to(device_point(0, 4));
path.line_to(device_point(5, 4));
path.line_to(device_point(11, 0));
path.line_to(device_point(11, 15));
path.line_to(device_point(5, 11));
path.line_to(device_point(0, 11));
path.line_to(device_point(0, 4));
path.close();
painter.fill_path(path, speaker_button_color, Gfx::Painter::WindingRule::EvenOdd);
path.clear();
path.move_to(device_point(13, 3));
path.quadratic_bezier_curve_to(device_point(16, 7.5), device_point(13, 12));
path.move_to(device_point(14, 0));
path.quadratic_bezier_curve_to(device_point(20, 7.5), device_point(14, 15));
painter.stroke_path(path, speaker_button_color, 1);
if (media_element.muted()) {
painter.draw_line(device_point(0, 0), device_point(20, 15), Color::Red, 2);
painter.draw_line(device_point(0, 15), device_point(20, 0), Color::Red, 2);
}
}
MediaPaintable::DispatchEventOfSameName MediaPaintable::handle_mouseup(Badge<EventHandler>, CSSPixelPoint position, unsigned button, unsigned)
{
if (button != GUI::MouseButton::Primary)
@ -221,6 +276,11 @@ MediaPaintable::DispatchEventOfSameName MediaPaintable::handle_mouseup(Badge<Eve
return DispatchEventOfSameName::Yes;
}
if (cached_layout_boxes.speaker_button_rect.has_value() && cached_layout_boxes.speaker_button_rect->contains(position)) {
media_element.set_muted(!media_element.muted());
return DispatchEventOfSameName::Yes;
}
return DispatchEventOfSameName::No;
}

View File

@ -34,6 +34,9 @@ private:
String timestamp;
RefPtr<Gfx::Font> timestamp_font;
DevicePixelRect timestamp_rect;
DevicePixelRect speaker_button_rect;
DevicePixels speaker_button_size;
};
virtual bool wants_mouse_events() const override { return true; }
@ -44,6 +47,7 @@ private:
static void paint_control_bar_playback_button(PaintContext&, HTML::HTMLMediaElement const&, Components const&, Optional<DevicePixelPoint> const& mouse_position);
static void paint_control_bar_timeline(PaintContext&, HTML::HTMLMediaElement const&, Components const&, Optional<DevicePixelPoint> const& mouse_position);
static void paint_control_bar_timestamp(PaintContext&, Components const&);
static void paint_control_bar_speaker(PaintContext&, HTML::HTMLMediaElement const&, Components const& components, Optional<DevicePixelPoint> const& mouse_position);
};
}