diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.cpp index 3f464e8e38..5ccf71895a 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.cpp @@ -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(); } diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.h b/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.h index 74ec7c65fb..c0c4b2d7b5 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLMediaElement.h @@ -103,6 +103,7 @@ public: Optional control_box_rect; Optional playback_button_rect; Optional timeline_rect; + Optional speaker_button_rect; }; CachedLayoutBoxes& cached_layout_boxes(Badge) const { return m_layout_boxes; } diff --git a/Userland/Libraries/LibWeb/Painting/MediaPaintable.cpp b/Userland/Libraries/LibWeb/Painting/MediaPaintable.cpp index d840a6d9c7..9996d4153e 100644 --- a/Userland/Libraries/LibWeb/Painting/MediaPaintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/MediaPaintable.cpp @@ -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(), 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 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().to_type(); + }; + + 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, CSSPixelPoint position, unsigned button, unsigned) { if (button != GUI::MouseButton::Primary) @@ -221,6 +276,11 @@ MediaPaintable::DispatchEventOfSameName MediaPaintable::handle_mouseup(Badgecontains(position)) { + media_element.set_muted(!media_element.muted()); + return DispatchEventOfSameName::Yes; + } + return DispatchEventOfSameName::No; } diff --git a/Userland/Libraries/LibWeb/Painting/MediaPaintable.h b/Userland/Libraries/LibWeb/Painting/MediaPaintable.h index 96d2309b31..73d75a0e7b 100644 --- a/Userland/Libraries/LibWeb/Painting/MediaPaintable.h +++ b/Userland/Libraries/LibWeb/Painting/MediaPaintable.h @@ -34,6 +34,9 @@ private: String timestamp; RefPtr 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 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&); + static void paint_control_bar_speaker(PaintContext&, HTML::HTMLMediaElement const&, Components const& components, Optional const& mouse_position); }; }