Skip to content

Commit

Permalink
layout: Make the stacking context take into account the children tran…
Browse files Browse the repository at this point in the history
…sform when calculating overflow areas.

This only works for simple translations and similar, but I want Patrick to
validate my approach.
  • Loading branch information
emilio committed Aug 16, 2016
1 parent 49431be commit fd3a99e
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 32 deletions.
101 changes: 76 additions & 25 deletions components/gfx/display_list/mod.rs
Expand Up @@ -360,7 +360,7 @@ impl DisplayList {
return Some(stacking_context);
}

for kid in stacking_context.children.iter() {
for kid in stacking_context.children() {
let result = find_stacking_context_in_stacking_context(kid, stacking_context_id);
if result.is_some() {
return result;
Expand Down Expand Up @@ -449,8 +449,9 @@ impl DisplayList {
let old_transform = paint_context.draw_target.get_transform();
let pixels_per_px = paint_context.screen_pixels_per_px();
let (transform, subpixel_offset) = match stacking_context.layer_info {
// If this stacking context starts a layer, the offset and transformation are handled
// by layer position within the compositor.
// If this stacking context starts a layer, the offset and
// transformation are handled by layer position within the
// compositor.
Some(..) => (*transform, *subpixel_offset),
None => {
let origin = stacking_context.bounds.origin + *subpixel_offset;
Expand Down Expand Up @@ -606,7 +607,7 @@ pub struct StackingContext {
pub layer_info: Option<LayerInfo>,

/// Children of this StackingContext.
pub children: Vec<Box<StackingContext>>,
children: Vec<Box<StackingContext>>,
}

impl StackingContext {
Expand Down Expand Up @@ -642,6 +643,73 @@ impl StackingContext {
}
}

pub fn set_children(&mut self, children: Vec<Box<StackingContext>>) {
debug_assert!(self.children.is_empty());
// We need to take into account the possible transformations of the
// child stacking contexts.
for child in &children {
self.update_overflow_for_new_child(&child);
}

self.children = children;
}

pub fn add_child(&mut self, child: Box<StackingContext>) {
self.update_overflow_for_new_child(&child);
self.children.push(child);
}

pub fn add_children(&mut self, children: Vec<Box<StackingContext>>) {
if self.children.is_empty() {
return self.set_children(children);
}

for child in children {
self.add_child(child);
}
}

pub fn child_at_mut(&mut self, index: usize) -> &mut StackingContext {
&mut *self.children[index]
}

pub fn children(&self) -> &[Box<StackingContext>] {
&self.children
}

fn update_overflow_for_new_child(&mut self, child: &StackingContext) {
if self.context_type == StackingContextType::Real &&
child.context_type == StackingContextType::Real &&
!self.scrolls_overflow_area {
// This child might be transformed, so we need to take into account
// its transformed overflow rect too, but at the correct position.
let overflow =
child.overflow_rect_in_parent_space();

self.overflow = self.overflow.union(&overflow);
}
}

fn overflow_rect_in_parent_space(&self) -> Rect<Au> {
// Transform this stacking context to get it into the same space as
// the parent stacking context.
//
// TODO: Take into account 3d transforms, even though it's a fairly
// uncommon case.
let origin_x = self.bounds.origin.x.to_f32_px();
let origin_y = self.bounds.origin.y.to_f32_px();

let transform = Matrix4D::identity().translate(origin_x, origin_y, 0.0)
.mul(&self.transform);
let transform_2d = Matrix2D::new(transform.m11, transform.m12,
transform.m21, transform.m22,
transform.m41, transform.m42);

let overflow = geometry::au_rect_to_f32_rect(self.overflow);
let overflow = transform_2d.transform_rect(&overflow);
geometry::f32_rect_to_au_rect(overflow)
}

pub fn hit_test<'a>(&self,
traversal: &mut DisplayListTraversal<'a>,
translated_point: &Point2D<Au>,
Expand Down Expand Up @@ -679,7 +747,7 @@ impl StackingContext {
}
}

for child in self.children.iter() {
for child in self.children() {
while let Some(item) = traversal.advance(self) {
if let Some(meta) = item.hit_test(point) {
result.push(meta);
Expand All @@ -697,13 +765,13 @@ impl StackingContext {

pub fn print_with_tree(&self, print_tree: &mut PrintTree) {
print_tree.new_level(format!("{:?}", self));
for kid in self.children.iter() {
for kid in self.children() {
kid.print_with_tree(print_tree);
}
print_tree.end_level();
}

pub fn intersects_rect_in_parent_context(&self, rect: Option<Rect<Au>>) -> bool {
fn intersects_rect_in_parent_context(&self, rect: Option<Rect<Au>>) -> bool {
// We only do intersection checks for real stacking contexts, since
// pseudo stacking contexts might not have proper position information.
if self.context_type != StackingContextType::Real {
Expand All @@ -715,24 +783,7 @@ impl StackingContext {
None => return true,
};

// Transform this stacking context to get it into the same space as
// the parent stacking context.
let origin_x = self.bounds.origin.x.to_f32_px();
let origin_y = self.bounds.origin.y.to_f32_px();

let transform = Matrix4D::identity().translate(origin_x,
origin_y,
0.0)
.mul(&self.transform);
let transform_2d = Matrix2D::new(transform.m11, transform.m12,
transform.m21, transform.m22,
transform.m41, transform.m42);

let overflow = geometry::au_rect_to_f32_rect(self.overflow);
let overflow = transform_2d.transform_rect(&overflow);
let overflow = geometry::f32_rect_to_au_rect(overflow);

rect.intersects(&overflow)
self.overflow_rect_in_parent_space().intersects(rect)
}
}

Expand Down
2 changes: 1 addition & 1 deletion components/gfx/paint_thread.rs
Expand Up @@ -254,7 +254,7 @@ impl LayerCreator {
parent_origin: &Point2D<Au>,
transform: &Matrix4D<f32>,
perspective: &Matrix4D<f32>) {
for kid in stacking_context.children.iter() {
for kid in stacking_context.children() {
while let Some(item) = traversal.advance(stacking_context) {
self.create_layers_for_item(item,
parent_origin,
Expand Down
8 changes: 4 additions & 4 deletions components/layout/display_list_builder.rs
Expand Up @@ -1729,7 +1729,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
}
}

contexts[stacking_context_index].children = floating;
contexts[stacking_context_index].set_children(floating);
return stacking_context_id;
}

Expand All @@ -1745,22 +1745,22 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
&self.base,
scroll_policy,
StackingContextCreationMode::InnerScrollWrapper);
inner_stacking_context.children = child_contexts;
inner_stacking_context.set_children(child_contexts);

let mut outer_stacking_context = self.fragment.create_stacking_context(
stacking_context_id,
&self.base,
scroll_policy,
StackingContextCreationMode::OuterScrollWrapper);
outer_stacking_context.children.push(inner_stacking_context);
outer_stacking_context.add_child(inner_stacking_context);
outer_stacking_context
} else {
let mut stacking_context = self.fragment.create_stacking_context(
stacking_context_id,
&self.base,
scroll_policy,
StackingContextCreationMode::Normal);
stacking_context.children = child_contexts;
stacking_context.set_children(child_contexts);
stacking_context
};

Expand Down
4 changes: 3 additions & 1 deletion components/layout/sequential.rs
Expand Up @@ -84,8 +84,10 @@ pub fn build_display_list_for_subtree(root: &mut FlowRef,
let flow_root = flow_ref::deref_mut(root);
let layout_context = LayoutContext::new(shared_layout_context);
flow_root.traverse_preorder(&ComputeAbsolutePositions { layout_context: &layout_context });
let mut children = vec![];
flow_root.collect_stacking_contexts(root_stacking_context.id,
&mut root_stacking_context.children);
&mut children);
root_stacking_context.add_children(children);
let mut build_display_list = BuildDisplayList {
state: DisplayListBuildState::new(&layout_context,
flow::base(&*flow_root).stacking_context_id),
Expand Down
2 changes: 1 addition & 1 deletion components/layout/webrender_helpers.rs
Expand Up @@ -260,7 +260,7 @@ impl WebRenderStackingContextConverter for StackingContext {
builder: &mut webrender_traits::DisplayListBuilder,
frame_builder: &mut WebRenderFrameBuilder,
_force_positioned_stacking_level: bool) {
for child in self.children.iter() {
for child in self.children() {
while let Some(item) = traversal.advance(self) {
item.convert_to_webrender(builder, frame_builder);
}
Expand Down

0 comments on commit fd3a99e

Please sign in to comment.