1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 22:48:11 +00:00

LibWeb: Apply scroll boxes offsets after painting commands recording

With this change, instead of applying scroll offsets during the
recording of the painting command list, we do the following:
1. Collect all boxes with scrollable overflow into a PaintContext,
   each with an id and the total amount of scrolling offset accumulated
   from ancestor scrollable boxes.
2. During the recording phase assign a corresponding scroll_frame_id to
   each command that paints content within a scrollable box.
3. Before executing the recorded commands, translate each command that
   has a scroll_frame_id by the accumulated scroll offset.

This approach has following advantages:
- Implementing nested scrollables becomes much simpler, as the
  recording phase only requires the correct assignment of the nearest
  scrollable's scroll_frame_id, while the accumulated offset from
  ancestors is applied subsequently.
- The recording of painting commands is not tied to a specific offset
  within scrollable boxes, which means in the future, it will be
  possible to update the scrolling offset and repaint without the need
  to re-record painting commands.
This commit is contained in:
Aliaksandr Kalenik 2023-12-29 06:10:32 +01:00 committed by Andreas Kling
parent d3025668a4
commit ac6b3c989d
12 changed files with 307 additions and 24 deletions

View file

@ -410,16 +410,17 @@ Optional<CSSPixelRect> PaintableBox::calculate_overflow_clipped_rect() const
void PaintableBox::apply_scroll_offset(PaintContext& context, PaintPhase) const
{
auto scroll_offset = -this->scroll_offset();
context.translate_scroll_offset_by(scroll_offset);
context.recording_painter().translate({ context.enclosing_device_pixels(scroll_offset.x()), context.enclosing_device_pixels(scroll_offset.y()) });
if (context.scroll_frames().contains(this)) {
context.recording_painter().save();
context.recording_painter().set_scroll_frame_id(context.scroll_frames().get(this)->id);
}
}
void PaintableBox::reset_scroll_offset(PaintContext& context, PaintPhase) const
{
auto scroll_offset = this->scroll_offset();
context.translate_scroll_offset_by(scroll_offset);
context.recording_painter().translate({ context.enclosing_device_pixels(scroll_offset.x()), context.enclosing_device_pixels(scroll_offset.y()) });
if (context.scroll_frames().contains(this)) {
context.recording_painter().restore();
}
}
void PaintableBox::apply_clip_overflow_rect(PaintContext& context, PaintPhase phase) const
@ -445,10 +446,7 @@ void PaintableBox::apply_clip_overflow_rect(PaintContext& context, PaintPhase ph
if (!m_clipping_overflow) {
context.recording_painter().save();
auto scroll_offset = context.scroll_offset();
context.recording_painter().translate({ -context.enclosing_device_pixels(scroll_offset.x()), -context.enclosing_device_pixels(scroll_offset.y()) });
context.recording_painter().add_clip_rect(context.enclosing_device_rect(*clip_rect).to_type<int>());
context.recording_painter().translate({ context.enclosing_device_pixels(scroll_offset.x()), context.enclosing_device_pixels(scroll_offset.y()) });
m_clipping_overflow = true;
}
@ -826,10 +824,12 @@ Optional<HitTestResult> PaintableWithLines::hit_test(CSSPixelPoint position, Hit
return {};
}
PaintableBox const* PaintableBox::nearest_scrollable_ancestor() const
PaintableBox const* PaintableBox::nearest_scrollable_ancestor_within_stacking_context() const
{
auto* ancestor = parent();
while (ancestor) {
if (ancestor->stacking_context_rooted_here())
return nullptr;
if (ancestor->is_paintable_box() && static_cast<PaintableBox const*>(ancestor)->has_scrollable_overflow())
return static_cast<PaintableBox const*>(ancestor);
ancestor = ancestor->parent();