mirror of
https://github.com/servo/servo.git
synced 2025-08-01 03:30:33 +01:00
auto merge of #4299 : pcwalton/servo/outline, r=mbrubeck
`invert` is not yet supported. Objects that get layers will not yet display outlines properly. This is because our overflow calculation doesn't take styles into account and because layers are always anchored to the top left of the border box. Since fixing this is work that is not related to outline *per se* I'm leaving that to a followup and making a note in the code. r? @SimonSapin
This commit is contained in:
commit
d31237f343
10 changed files with 242 additions and 6 deletions
|
@ -75,6 +75,8 @@ pub struct DisplayList {
|
|||
pub floats: DList<DisplayItem>,
|
||||
/// All other content.
|
||||
pub content: DList<DisplayItem>,
|
||||
/// Outlines: step 10.
|
||||
pub outlines: DList<DisplayItem>,
|
||||
/// Child stacking contexts.
|
||||
pub children: DList<Arc<StackingContext>>,
|
||||
}
|
||||
|
@ -88,6 +90,7 @@ impl DisplayList {
|
|||
block_backgrounds_and_borders: DList::new(),
|
||||
floats: DList::new(),
|
||||
content: DList::new(),
|
||||
outlines: DList::new(),
|
||||
children: DList::new(),
|
||||
}
|
||||
}
|
||||
|
@ -102,12 +105,14 @@ impl DisplayList {
|
|||
&mut other.block_backgrounds_and_borders);
|
||||
servo_dlist::append_from(&mut self.floats, &mut other.floats);
|
||||
servo_dlist::append_from(&mut self.content, &mut other.content);
|
||||
servo_dlist::append_from(&mut self.outlines, &mut other.outlines);
|
||||
servo_dlist::append_from(&mut self.children, &mut other.children);
|
||||
}
|
||||
|
||||
/// Merges all display items from all non-float stacking levels to the `float` stacking level.
|
||||
#[inline]
|
||||
pub fn form_float_pseudo_stacking_context(&mut self) {
|
||||
servo_dlist::prepend_from(&mut self.floats, &mut self.outlines);
|
||||
servo_dlist::prepend_from(&mut self.floats, &mut self.content);
|
||||
servo_dlist::prepend_from(&mut self.floats, &mut self.block_backgrounds_and_borders);
|
||||
servo_dlist::prepend_from(&mut self.floats, &mut self.background_and_borders);
|
||||
|
@ -129,6 +134,9 @@ impl DisplayList {
|
|||
for display_item in self.content.iter() {
|
||||
result.push((*display_item).clone())
|
||||
}
|
||||
for display_item in self.outlines.iter() {
|
||||
result.push((*display_item).clone())
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
@ -282,7 +290,10 @@ impl StackingContext {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(pcwalton): Step 10: Outlines.
|
||||
// Step 10: Outlines.
|
||||
for display_item in display_list.outlines.iter() {
|
||||
display_item.draw_into_context(&mut paint_subcontext)
|
||||
}
|
||||
|
||||
// Undo our clipping and transform.
|
||||
if paint_subcontext.transient_clip_rect.is_some() {
|
||||
|
@ -349,6 +360,12 @@ impl StackingContext {
|
|||
// Iterate through display items in reverse stacking order. Steps here refer to the
|
||||
// painting steps in CSS 2.1 Appendix E.
|
||||
//
|
||||
// Step 10: Outlines.
|
||||
hit_test_in_list(point, result, topmost_only, self.display_list.outlines.iter().rev());
|
||||
if topmost_only && !result.is_empty() {
|
||||
return
|
||||
}
|
||||
|
||||
// Steps 9 and 8: Positioned descendants with nonnegative z-indices.
|
||||
for kid in self.display_list.children.iter().rev() {
|
||||
if kid.z_index < 0 {
|
||||
|
|
|
@ -35,6 +35,7 @@ impl DisplayListOptimizer {
|
|||
display_list.block_backgrounds_and_borders.iter());
|
||||
self.add_in_bounds_display_items(&mut result.floats, display_list.floats.iter());
|
||||
self.add_in_bounds_display_items(&mut result.content, display_list.content.iter());
|
||||
self.add_in_bounds_display_items(&mut result.outlines, display_list.outlines.iter());
|
||||
self.add_in_bounds_stacking_contexts(&mut result.children, display_list.children.iter());
|
||||
result
|
||||
}
|
||||
|
|
|
@ -112,6 +112,12 @@ pub trait FragmentDisplayListBuilding {
|
|||
level: StackingLevel,
|
||||
clip_rect: &Rect<Au>);
|
||||
|
||||
fn build_display_list_for_outline_if_applicable(&self,
|
||||
style: &ComputedValues,
|
||||
display_list: &mut DisplayList,
|
||||
bounds: &Rect<Au>,
|
||||
clip_rect: &Rect<Au>);
|
||||
|
||||
fn build_debug_borders_around_text_fragments(&self,
|
||||
display_list: &mut DisplayList,
|
||||
flow_origin: Point2D<Au>,
|
||||
|
@ -439,6 +445,40 @@ impl FragmentDisplayListBuilding for Fragment {
|
|||
}), level);
|
||||
}
|
||||
|
||||
fn build_display_list_for_outline_if_applicable(&self,
|
||||
style: &ComputedValues,
|
||||
display_list: &mut DisplayList,
|
||||
bounds: &Rect<Au>,
|
||||
clip_rect: &Rect<Au>) {
|
||||
let width = style.get_outline().outline_width;
|
||||
if width == Au(0) {
|
||||
return
|
||||
}
|
||||
|
||||
let outline_style = style.get_outline().outline_style;
|
||||
if outline_style == border_style::none {
|
||||
return
|
||||
}
|
||||
|
||||
// Outlines are not accounted for in the dimensions of the border box, so adjust the
|
||||
// absolute bounds.
|
||||
let mut bounds = *bounds;
|
||||
bounds.origin.x = bounds.origin.x - width;
|
||||
bounds.origin.y = bounds.origin.y - width;
|
||||
bounds.size.width = bounds.size.width + width + width;
|
||||
bounds.size.height = bounds.size.height + width + width;
|
||||
|
||||
// Append the outline to the display list.
|
||||
let color = style.resolve_color(style.get_outline().outline_color).to_gfx_color();
|
||||
display_list.outlines.push_back(BorderDisplayItemClass(box BorderDisplayItem {
|
||||
base: BaseDisplayItem::new(bounds, self.node, *clip_rect),
|
||||
border_widths: SideOffsets2D::new_all_same(width),
|
||||
color: SideOffsets2D::new_all_same(color),
|
||||
style: SideOffsets2D::new_all_same(outline_style),
|
||||
radius: Default::default(),
|
||||
}))
|
||||
}
|
||||
|
||||
fn build_debug_borders_around_text_fragments(&self,
|
||||
display_list: &mut DisplayList,
|
||||
flow_origin: Point2D<Au>,
|
||||
|
@ -578,9 +618,7 @@ impl FragmentDisplayListBuilding for Fragment {
|
|||
}
|
||||
}
|
||||
|
||||
// Add a border, if applicable.
|
||||
//
|
||||
// TODO: Outlines.
|
||||
// Add a border and outlines, if applicable.
|
||||
match self.inline_context {
|
||||
Some(ref inline_context) => {
|
||||
for style in inline_context.styles.iter().rev() {
|
||||
|
@ -590,6 +628,11 @@ impl FragmentDisplayListBuilding for Fragment {
|
|||
&absolute_fragment_bounds,
|
||||
level,
|
||||
clip_rect);
|
||||
self.build_display_list_for_outline_if_applicable(
|
||||
&**style,
|
||||
display_list,
|
||||
&absolute_fragment_bounds,
|
||||
clip_rect);
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
|
@ -603,6 +646,11 @@ impl FragmentDisplayListBuilding for Fragment {
|
|||
&absolute_fragment_bounds,
|
||||
level,
|
||||
clip_rect);
|
||||
self.build_display_list_for_outline_if_applicable(
|
||||
&*self.style,
|
||||
display_list,
|
||||
&absolute_fragment_bounds,
|
||||
clip_rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -870,8 +918,8 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
|
|||
let stacking_context =
|
||||
self.create_stacking_context(display_list,
|
||||
Some(Arc::new(PaintLayer::new(self.layer_id(0),
|
||||
transparent,
|
||||
scroll_policy))));
|
||||
transparent,
|
||||
scroll_policy))));
|
||||
self.base.display_list_building_result = StackingContextResult(stacking_context)
|
||||
}
|
||||
|
||||
|
|
|
@ -1177,6 +1177,11 @@ impl<'a> MutableFlowUtils for &'a mut Flow + 'a {
|
|||
/// Assumption: Absolute descendants have had their overflow calculated.
|
||||
fn store_overflow(self, _: &LayoutContext) {
|
||||
let my_position = mut_base(self).position;
|
||||
|
||||
// FIXME(pcwalton): We should calculate overflow on a per-fragment basis, because their
|
||||
// styles can affect overflow regions. Consider `box-shadow`, `outline`, etc.--anything
|
||||
// that can draw outside the border box. For now we assume overflow is the border box, but
|
||||
// that is wrong.
|
||||
let mut overflow = my_position;
|
||||
|
||||
if self.is_block_container() {
|
||||
|
|
|
@ -355,6 +355,38 @@ pub mod longhands {
|
|||
</%self:longhand>
|
||||
% endfor
|
||||
|
||||
${new_style_struct("Outline", is_inherited=False)}
|
||||
|
||||
// TODO(pcwalton): `invert`
|
||||
${predefined_type("outline-color", "CSSColor", "CurrentColor")}
|
||||
|
||||
<%self:single_component_value name="outline-style">
|
||||
pub use super::border_top_style::{get_initial_value, to_computed_value};
|
||||
pub type SpecifiedValue = super::border_top_style::SpecifiedValue;
|
||||
pub mod computed_value {
|
||||
pub type T = super::super::border_top_style::computed_value::T;
|
||||
}
|
||||
pub fn from_component_value(value: &ComponentValue, base_url: &Url)
|
||||
-> Result<SpecifiedValue,()> {
|
||||
match value {
|
||||
&Ident(ref ident) if ident.eq_ignore_ascii_case("hidden") => {
|
||||
// `hidden` is not a valid value.
|
||||
Err(())
|
||||
}
|
||||
_ => super::border_top_style::from_component_value(value, base_url)
|
||||
}
|
||||
}
|
||||
</%self:single_component_value>
|
||||
|
||||
<%self:longhand name="outline-width">
|
||||
pub use super::border_top_width::{get_initial_value, parse};
|
||||
pub use computed::compute_Au as to_computed_value;
|
||||
pub type SpecifiedValue = super::border_top_width::SpecifiedValue;
|
||||
pub mod computed_value {
|
||||
pub type T = super::super::border_top_width::computed_value::T;
|
||||
}
|
||||
</%self:longhand>
|
||||
|
||||
${new_style_struct("PositionOffsets", is_inherited=False)}
|
||||
|
||||
% for side in ["top", "right", "bottom", "left"]:
|
||||
|
@ -1544,6 +1576,52 @@ pub mod shorthands {
|
|||
})
|
||||
</%self:shorthand>
|
||||
|
||||
<%self:shorthand name="outline" sub_properties="outline-color outline-style outline-width">
|
||||
let (mut color, mut style, mut width, mut any) = (None, None, None, false);
|
||||
for component_value in input.skip_whitespace() {
|
||||
if color.is_none() {
|
||||
match specified::CSSColor::parse(component_value) {
|
||||
Ok(c) => {
|
||||
color = Some(c);
|
||||
any = true;
|
||||
continue
|
||||
}
|
||||
Err(()) => {}
|
||||
}
|
||||
}
|
||||
if style.is_none() {
|
||||
match border_top_style::from_component_value(component_value, base_url) {
|
||||
Ok(s) => {
|
||||
style = Some(s);
|
||||
any = true;
|
||||
continue
|
||||
}
|
||||
Err(()) => {}
|
||||
}
|
||||
}
|
||||
if width.is_none() {
|
||||
match parse_border_width(component_value, base_url) {
|
||||
Ok(w) => {
|
||||
width = Some(w);
|
||||
any = true;
|
||||
continue
|
||||
}
|
||||
Err(()) => {}
|
||||
}
|
||||
}
|
||||
return Err(())
|
||||
}
|
||||
if any {
|
||||
Ok(Longhands {
|
||||
outline_color: color,
|
||||
outline_style: style,
|
||||
outline_width: width,
|
||||
})
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
</%self:shorthand>
|
||||
|
||||
<%self:shorthand name="font" sub_properties="font-style font-variant font-weight
|
||||
font-size line-height font-family">
|
||||
let mut iter = input.skip_whitespace();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue