LibWeb: Update the media display when the hovered component changes

This allows the media paintable to be redrawn when the media element is
paused. We change the color of some components on hover, but if the
media was paused, that would not be rendered. This wasn't an issue while
the media is playing because the update to the timeline would take care
of redrawing the paintable.
This commit is contained in:
Timothy Flynn 2025-04-22 13:06:45 -04:00 committed by Andreas Kling
parent 7b0e62cae7
commit 8417ade552
Notes: github-actions[bot] 2025-04-23 11:35:51 +00:00
3 changed files with 40 additions and 13 deletions

View file

@ -132,12 +132,18 @@ public:
WebIDL::ExceptionOr<bool> handle_keydown(Badge<Web::EventHandler>, UIEvents::KeyCode, u32 modifiers);
enum class MouseTrackingComponent {
enum class MediaComponent {
PlaybackButton,
SpeakerButton,
Timeline,
Volume,
};
void set_layout_mouse_tracking_component(Badge<Painting::MediaPaintable>, Optional<MouseTrackingComponent> mouse_tracking_component) { m_mouse_tracking_component = move(mouse_tracking_component); }
Optional<MouseTrackingComponent> const& layout_mouse_tracking_component(Badge<Painting::MediaPaintable>) const { return m_mouse_tracking_component; }
void set_layout_mouse_tracking_component(Badge<Painting::MediaPaintable>, Optional<MediaComponent> mouse_tracking_component) { m_mouse_tracking_component = move(mouse_tracking_component); }
Optional<MediaComponent> const& layout_mouse_tracking_component(Badge<Painting::MediaPaintable>) const { return m_mouse_tracking_component; }
void set_layout_hovered_component(Badge<Painting::MediaPaintable>, Optional<MediaComponent> hovered_component) { m_hovered_component = hovered_component; }
Optional<MediaComponent> const& layout_hovered_component(Badge<Painting::MediaPaintable>) const { return m_hovered_component; }
void set_layout_mouse_position(Badge<Painting::MediaPaintable>, Optional<CSSPixelPoint> mouse_position) { m_mouse_position = move(mouse_position); }
Optional<CSSPixelPoint> const& layout_mouse_position(Badge<Painting::MediaPaintable>) const { return m_mouse_position; }
@ -318,7 +324,8 @@ private:
bool m_seek_in_progress = false;
// Cached state for layout.
Optional<MouseTrackingComponent> m_mouse_tracking_component;
Optional<MediaComponent> m_mouse_tracking_component;
Optional<MediaComponent> m_hovered_component;
bool m_tracking_mouse_position_while_playing { false };
Optional<CSSPixelPoint> m_mouse_position;
Optional<double> m_display_time;

View file

@ -272,7 +272,7 @@ void MediaPaintable::paint_control_bar_volume(PaintContext& context, HTML::HTMLM
volume_button_rect.shrink(components.volume_scrub_rect.width() - components.volume_button_size, components.volume_scrub_rect.height() - components.volume_button_size);
volume_button_rect.set_x(components.volume_scrub_rect.x() + volume_button_offset_x - components.volume_button_size / 2);
auto volume_is_hovered = rect_is_hovered(media_element, components.volume_rect, mouse_position, HTML::HTMLMediaElement::MouseTrackingComponent::Volume);
auto volume_is_hovered = rect_is_hovered(media_element, components.volume_rect, mouse_position, HTML::HTMLMediaElement::MediaComponent::Volume);
auto volume_color = control_button_color(volume_is_hovered);
context.display_list_recorder().fill_ellipse(volume_button_rect.to_type<int>(), volume_color);
}
@ -289,10 +289,10 @@ MediaPaintable::DispatchEventOfSameName MediaPaintable::handle_mousedown(Badge<E
position_adjusted_by_scroll_offset.translate_by(-cumulative_offset_of_enclosing_scroll_frame());
if (cached_layout_boxes.timeline_rect.has_value() && cached_layout_boxes.timeline_rect->contains(position_adjusted_by_scroll_offset)) {
media_element.set_layout_mouse_tracking_component({}, HTML::HTMLMediaElement::MouseTrackingComponent::Timeline);
media_element.set_layout_mouse_tracking_component({}, HTML::HTMLMediaElement::MediaComponent::Timeline);
set_current_time(media_element, *cached_layout_boxes.timeline_rect, position_adjusted_by_scroll_offset, Temporary::Yes);
} else if (cached_layout_boxes.volume_rect.has_value() && cached_layout_boxes.volume_rect->contains(position_adjusted_by_scroll_offset)) {
media_element.set_layout_mouse_tracking_component({}, HTML::HTMLMediaElement::MouseTrackingComponent::Volume);
media_element.set_layout_mouse_tracking_component({}, HTML::HTMLMediaElement::MediaComponent::Volume);
set_volume(media_element, *cached_layout_boxes.volume_scrub_rect, position_adjusted_by_scroll_offset);
}
@ -312,14 +312,17 @@ MediaPaintable::DispatchEventOfSameName MediaPaintable::handle_mouseup(Badge<Eve
if (auto const& mouse_tracking_component = media_element.layout_mouse_tracking_component({}); mouse_tracking_component.has_value()) {
switch (*mouse_tracking_component) {
case HTML::HTMLMediaElement::MouseTrackingComponent::Timeline:
case HTML::HTMLMediaElement::MediaComponent::Timeline:
set_current_time(media_element, *cached_layout_boxes.timeline_rect, position_adjusted_by_scroll_offset, Temporary::No);
media_element.set_layout_display_time({}, {});
break;
case HTML::HTMLMediaElement::MouseTrackingComponent::Volume:
case HTML::HTMLMediaElement::MediaComponent::Volume:
document().page().client().page_did_stop_tooltip_override();
break;
default:
VERIFY_NOT_REACHED();
}
const_cast<HTML::Navigable&>(*navigable()).event_handler().set_mouse_event_tracking_paintable(nullptr);
@ -361,12 +364,12 @@ MediaPaintable::DispatchEventOfSameName MediaPaintable::handle_mousemove(Badge<E
if (auto const& mouse_tracking_component = media_element.layout_mouse_tracking_component({}); mouse_tracking_component.has_value()) {
switch (*mouse_tracking_component) {
case HTML::HTMLMediaElement::MouseTrackingComponent::Timeline:
case HTML::HTMLMediaElement::MediaComponent::Timeline:
if (cached_layout_boxes.timeline_rect.has_value())
set_current_time(media_element, *cached_layout_boxes.timeline_rect, position_adjusted_by_scroll_offset, Temporary::Yes);
break;
case HTML::HTMLMediaElement::MouseTrackingComponent::Volume:
case HTML::HTMLMediaElement::MediaComponent::Volume:
if (cached_layout_boxes.volume_rect.has_value()) {
set_volume(media_element, *cached_layout_boxes.volume_scrub_rect, position_adjusted_by_scroll_offset);
@ -375,9 +378,26 @@ MediaPaintable::DispatchEventOfSameName MediaPaintable::handle_mousemove(Badge<E
}
break;
default:
VERIFY_NOT_REACHED();
}
}
auto previous_hovered_component = media_element.layout_hovered_component({});
if (cached_layout_boxes.playback_button_rect.has_value() && cached_layout_boxes.playback_button_rect->contains(position_adjusted_by_scroll_offset))
media_element.set_layout_hovered_component({}, HTML::HTMLMediaElement::MediaComponent::PlaybackButton);
else if (cached_layout_boxes.speaker_button_rect.has_value() && cached_layout_boxes.speaker_button_rect->contains(position_adjusted_by_scroll_offset))
media_element.set_layout_hovered_component({}, HTML::HTMLMediaElement::MediaComponent::SpeakerButton);
else if (cached_layout_boxes.volume_rect.has_value() && cached_layout_boxes.volume_rect->contains(position_adjusted_by_scroll_offset))
media_element.set_layout_hovered_component({}, HTML::HTMLMediaElement::MediaComponent::Volume);
else
media_element.set_layout_hovered_component({}, {});
if (previous_hovered_component != media_element.layout_hovered_component({}))
set_needs_display();
if (absolute_rect().contains(position_adjusted_by_scroll_offset)) {
media_element.set_layout_mouse_position({}, position_adjusted_by_scroll_offset);
return DispatchEventOfSameName::Yes;
@ -416,7 +436,7 @@ void MediaPaintable::set_volume(HTML::HTMLMediaElement& media_element, CSSPixelR
media_element.set_volume(volume).release_value_but_fixme_should_propagate_errors();
}
bool MediaPaintable::rect_is_hovered(HTML::HTMLMediaElement const& media_element, Optional<DevicePixelRect> const& rect, Optional<DevicePixelPoint> const& mouse_position, Optional<HTML::HTMLMediaElement::MouseTrackingComponent> const& allowed_mouse_tracking_component)
bool MediaPaintable::rect_is_hovered(HTML::HTMLMediaElement const& media_element, Optional<DevicePixelRect> const& rect, Optional<DevicePixelPoint> const& mouse_position, Optional<HTML::HTMLMediaElement::MediaComponent> const& allowed_mouse_tracking_component)
{
if (auto const& mouse_tracking_component = media_element.layout_mouse_tracking_component({}); mouse_tracking_component.has_value())
return mouse_tracking_component == allowed_mouse_tracking_component;

View file

@ -61,7 +61,7 @@ private:
static void set_current_time(HTML::HTMLMediaElement& media_element, CSSPixelRect timeline_rect, CSSPixelPoint mouse_position, Temporary);
static void set_volume(HTML::HTMLMediaElement& media_element, CSSPixelRect volume_rect, CSSPixelPoint mouse_position);
static bool rect_is_hovered(HTML::HTMLMediaElement const& media_element, Optional<DevicePixelRect> const& rect, Optional<DevicePixelPoint> const& mouse_position, Optional<HTML::HTMLMediaElement::MouseTrackingComponent> const& allowed_mouse_tracking_component = {});
static bool rect_is_hovered(HTML::HTMLMediaElement const& media_element, Optional<DevicePixelRect> const& rect, Optional<DevicePixelPoint> const& mouse_position, Optional<HTML::HTMLMediaElement::MediaComponent> const& allowed_mouse_tracking_component = {});
};
}