2015-04-29 03:48:45 +03:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
//! CSS Multi-column layout http://dev.w3.org/csswg/css-multicol/
|
|
|
|
|
|
|
|
#![deny(unsafe_code)]
|
|
|
|
|
2017-07-19 16:03:17 +03:00
|
|
|
use ServoArc;
|
2015-10-01 00:19:33 +03:00
|
|
|
use app_units::Au;
|
2015-04-29 03:48:45 +03:00
|
|
|
use block::BlockFlow;
|
2017-02-08 04:16:05 +03:00
|
|
|
use context::LayoutContext;
|
2018-01-07 17:53:18 +03:00
|
|
|
use display_list::{DisplayListBuildState, StackingContextCollectionState};
|
2017-06-14 17:25:05 +03:00
|
|
|
use euclid::{Point2D, Vector2D};
|
2015-04-29 03:48:45 +03:00
|
|
|
use floats::FloatKind;
|
2017-12-15 21:44:50 +03:00
|
|
|
use flow::{Flow, FlowClass, OpaqueFlow, FragmentationContext, GetBaseFlow};
|
2016-02-05 08:28:50 +03:00
|
|
|
use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
|
2016-07-04 22:57:00 +03:00
|
|
|
use gfx_traits::print_tree::PrintTree;
|
2016-01-29 22:19:14 +03:00
|
|
|
use std::cmp::{min, max};
|
2015-04-29 03:48:45 +03:00
|
|
|
use std::fmt;
|
|
|
|
use std::sync::Arc;
|
2016-02-18 12:21:29 +03:00
|
|
|
use style::logical_geometry::LogicalSize;
|
2017-07-18 05:44:25 +03:00
|
|
|
use style::properties::ComputedValues;
|
2016-12-02 10:03:37 +03:00
|
|
|
use style::values::Either;
|
2016-01-29 22:19:14 +03:00
|
|
|
use style::values::computed::{LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
|
2018-02-22 12:30:58 +03:00
|
|
|
use style::values::generics::column::ColumnCount;
|
2015-04-29 03:48:45 +03:00
|
|
|
|
2017-10-14 05:43:09 +03:00
|
|
|
#[allow(unsafe_code)]
|
|
|
|
unsafe impl ::flow::HasBaseFlow for MulticolFlow {}
|
|
|
|
|
|
|
|
#[repr(C)]
|
2015-04-29 03:48:45 +03:00
|
|
|
pub struct MulticolFlow {
|
|
|
|
pub block_flow: BlockFlow,
|
2016-01-29 22:19:14 +03:00
|
|
|
|
|
|
|
/// Length between the inline-start edge of a column and that of the next.
|
|
|
|
/// That is, the used column-width + used column-gap.
|
|
|
|
pub column_pitch: Au,
|
|
|
|
}
|
|
|
|
|
2017-10-14 05:43:09 +03:00
|
|
|
#[allow(unsafe_code)]
|
|
|
|
unsafe impl ::flow::HasBaseFlow for MulticolColumnFlow {}
|
|
|
|
|
|
|
|
#[repr(C)]
|
2016-01-29 22:19:14 +03:00
|
|
|
pub struct MulticolColumnFlow {
|
|
|
|
pub block_flow: BlockFlow,
|
2015-04-29 03:48:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl MulticolFlow {
|
2015-08-07 07:11:04 +03:00
|
|
|
pub fn from_fragment(fragment: Fragment, float_kind: Option<FloatKind>) -> MulticolFlow {
|
2015-04-29 03:48:45 +03:00
|
|
|
MulticolFlow {
|
2016-10-27 04:14:04 +03:00
|
|
|
block_flow: BlockFlow::from_fragment_and_float_kind(fragment, float_kind),
|
2016-01-29 22:19:14 +03:00
|
|
|
column_pitch: Au(0),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MulticolColumnFlow {
|
|
|
|
pub fn from_fragment(fragment: Fragment) -> MulticolColumnFlow {
|
|
|
|
MulticolColumnFlow {
|
2016-10-27 04:14:04 +03:00
|
|
|
block_flow: BlockFlow::from_fragment(fragment),
|
2015-04-29 03:48:45 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Flow for MulticolFlow {
|
|
|
|
fn class(&self) -> FlowClass {
|
|
|
|
FlowClass::Multicol
|
|
|
|
}
|
|
|
|
|
2016-01-29 22:19:14 +03:00
|
|
|
fn as_mut_block(&mut self) -> &mut BlockFlow {
|
|
|
|
&mut self.block_flow
|
|
|
|
}
|
|
|
|
|
|
|
|
fn as_block(&self) -> &BlockFlow {
|
|
|
|
&self.block_flow
|
|
|
|
}
|
|
|
|
|
2015-09-04 16:59:04 +03:00
|
|
|
fn as_mut_multicol(&mut self) -> &mut MulticolFlow {
|
2015-04-29 03:48:45 +03:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2016-01-29 22:19:14 +03:00
|
|
|
fn bubble_inline_sizes(&mut self) {
|
|
|
|
// FIXME(SimonSapin) http://dev.w3.org/csswg/css-sizing/#multicol-intrinsic
|
|
|
|
self.block_flow.bubble_inline_sizes();
|
|
|
|
}
|
|
|
|
|
2017-02-08 04:16:05 +03:00
|
|
|
fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
|
2016-01-29 22:19:14 +03:00
|
|
|
debug!("assign_inline_sizes({}): assigning inline_size for flow", "multicol");
|
2017-02-08 04:16:05 +03:00
|
|
|
let shared_context = layout_context.shared_context();
|
2016-06-23 11:03:02 +03:00
|
|
|
self.block_flow.compute_inline_sizes(shared_context);
|
2016-01-29 22:19:14 +03:00
|
|
|
|
|
|
|
// Move in from the inline-start border edge.
|
|
|
|
let inline_start_content_edge = self.block_flow.fragment.border_box.start.i +
|
|
|
|
self.block_flow.fragment.border_padding.inline_start;
|
|
|
|
|
|
|
|
// Distance from the inline-end margin edge to the inline-end content edge.
|
|
|
|
let inline_end_content_edge =
|
|
|
|
self.block_flow.fragment.margin.inline_end +
|
|
|
|
self.block_flow.fragment.border_padding.inline_end;
|
|
|
|
|
2017-02-08 04:16:05 +03:00
|
|
|
self.block_flow.assign_inline_sizes(layout_context);
|
2016-01-29 22:19:14 +03:00
|
|
|
let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end();
|
|
|
|
let content_inline_size =
|
|
|
|
self.block_flow.fragment.border_box.size.inline - padding_and_borders;
|
|
|
|
let column_width;
|
|
|
|
{
|
|
|
|
let column_style = self.block_flow.fragment.style.get_column();
|
|
|
|
|
2017-09-23 21:26:02 +03:00
|
|
|
let column_gap = match column_style.column_gap {
|
2018-04-07 02:48:59 +03:00
|
|
|
Either::First(len) => len.0.to_pixel_length(content_inline_size).into(),
|
2017-09-23 21:26:02 +03:00
|
|
|
Either::Second(_normal) => self.block_flow.fragment.style.get_font().font_size.size(),
|
|
|
|
};
|
2016-12-11 19:03:16 +03:00
|
|
|
|
2016-01-29 22:19:14 +03:00
|
|
|
let mut column_count;
|
2016-12-02 10:03:37 +03:00
|
|
|
if let Either::First(column_width) = column_style.column_width {
|
2017-09-13 13:20:20 +03:00
|
|
|
let column_width = Au::from(column_width);
|
2016-01-29 22:19:14 +03:00
|
|
|
column_count =
|
2017-09-13 13:20:20 +03:00
|
|
|
max(1, (content_inline_size + column_gap).0 / (column_width + column_gap).0);
|
2018-02-22 12:30:58 +03:00
|
|
|
if let ColumnCount::Integer(specified_column_count) = column_style.column_count {
|
2017-08-07 19:05:17 +03:00
|
|
|
column_count = min(column_count, specified_column_count.0 as i32);
|
2016-01-29 22:19:14 +03:00
|
|
|
}
|
|
|
|
} else {
|
2017-03-18 01:15:01 +03:00
|
|
|
column_count = match column_style.column_count {
|
2018-02-22 12:30:58 +03:00
|
|
|
ColumnCount::Integer(n) => n.0,
|
2017-03-18 01:15:01 +03:00
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
2016-01-29 22:19:14 +03:00
|
|
|
}
|
|
|
|
column_width =
|
|
|
|
max(Au(0), (content_inline_size + column_gap) / column_count - column_gap);
|
|
|
|
self.column_pitch = column_width + column_gap;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.block_flow.fragment.border_box.size.inline = content_inline_size + padding_and_borders;
|
|
|
|
|
|
|
|
self.block_flow.propagate_assigned_inline_size_to_children(
|
2016-06-23 11:03:02 +03:00
|
|
|
shared_context, inline_start_content_edge, inline_end_content_edge, column_width,
|
2016-01-29 22:19:14 +03:00
|
|
|
|_, _, _, _, _, _| {});
|
|
|
|
}
|
|
|
|
|
2017-02-08 04:16:05 +03:00
|
|
|
fn assign_block_size(&mut self, ctx: &LayoutContext) {
|
2016-01-29 22:19:14 +03:00
|
|
|
debug!("assign_block_size: assigning block_size for multicol");
|
|
|
|
|
|
|
|
let fragmentation_context = Some(FragmentationContext {
|
|
|
|
this_fragment_is_empty: true,
|
|
|
|
available_block_size: {
|
|
|
|
let style = &self.block_flow.fragment.style;
|
|
|
|
if let LengthOrPercentageOrAuto::Length(length) = style.content_block_size() {
|
2017-09-13 13:20:20 +03:00
|
|
|
Au::from(length)
|
2016-01-29 22:19:14 +03:00
|
|
|
} else if let LengthOrPercentageOrNone::Length(length) = style.max_block_size() {
|
2017-09-13 13:20:20 +03:00
|
|
|
Au::from(length)
|
2016-01-29 22:19:14 +03:00
|
|
|
} else {
|
|
|
|
// FIXME: do column balancing instead
|
|
|
|
// FIXME: (until column balancing) substract margins/borders/padding
|
|
|
|
LogicalSize::from_physical(
|
|
|
|
self.block_flow.base.writing_mode,
|
2017-03-11 15:40:31 +03:00
|
|
|
ctx.shared_context().viewport_size(),
|
2016-01-29 22:19:14 +03:00
|
|
|
).block
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Before layout, everything is in a single "column"
|
2018-01-26 03:06:33 +03:00
|
|
|
assert_eq!(self.block_flow.base.children.len(), 1);
|
2016-11-05 00:53:38 +03:00
|
|
|
let mut column = self.block_flow.base.children.pop_front_arc().unwrap();
|
2016-01-29 22:19:14 +03:00
|
|
|
|
|
|
|
// Pretend there is no children for this:
|
|
|
|
self.block_flow.assign_block_size(ctx);
|
|
|
|
|
|
|
|
loop {
|
2016-11-05 00:53:38 +03:00
|
|
|
let remaining = Arc::get_mut(&mut column).unwrap().fragment(ctx, fragmentation_context);
|
|
|
|
self.block_flow.base.children.push_back_arc(column);
|
2016-01-29 22:19:14 +03:00
|
|
|
column = match remaining {
|
|
|
|
Some(remaining) => remaining,
|
|
|
|
None => break
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-08 22:04:23 +03:00
|
|
|
fn compute_stacking_relative_position(&mut self, layout_context: &LayoutContext) {
|
|
|
|
self.block_flow.compute_stacking_relative_position(layout_context);
|
2016-01-29 22:19:14 +03:00
|
|
|
let pitch = LogicalSize::new(self.block_flow.base.writing_mode, self.column_pitch, Au(0));
|
|
|
|
let pitch = pitch.to_physical(self.block_flow.base.writing_mode);
|
|
|
|
for (i, child) in self.block_flow.base.children.iter_mut().enumerate() {
|
2017-12-15 21:44:50 +03:00
|
|
|
let point = &mut child.mut_base().stacking_relative_position;
|
2017-06-14 17:25:05 +03:00
|
|
|
*point = *point + Vector2D::new(pitch.width * i as i32, pitch.height * i as i32);
|
2016-01-29 22:19:14 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
|
|
|
|
self.block_flow.update_late_computed_inline_position_if_necessary(inline_position)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
|
|
|
|
self.block_flow.update_late_computed_block_position_if_necessary(block_position)
|
|
|
|
}
|
|
|
|
|
2016-03-02 03:51:08 +03:00
|
|
|
fn build_display_list(&mut self, state: &mut DisplayListBuildState) {
|
2016-01-29 22:19:14 +03:00
|
|
|
debug!("build_display_list_multicol");
|
2016-03-02 03:51:08 +03:00
|
|
|
self.block_flow.build_display_list(state);
|
|
|
|
}
|
|
|
|
|
2017-09-15 22:43:40 +03:00
|
|
|
fn collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState) {
|
2017-01-10 15:48:31 +03:00
|
|
|
self.block_flow.collect_stacking_contexts(state);
|
2016-01-29 22:19:14 +03:00
|
|
|
}
|
|
|
|
|
2017-07-19 16:03:17 +03:00
|
|
|
fn repair_style(&mut self, new_style: &ServoArc<ComputedValues>) {
|
2016-01-29 22:19:14 +03:00
|
|
|
self.block_flow.repair_style(new_style)
|
|
|
|
}
|
|
|
|
|
2016-02-05 08:28:50 +03:00
|
|
|
fn compute_overflow(&self) -> Overflow {
|
2016-01-29 22:19:14 +03:00
|
|
|
self.block_flow.compute_overflow()
|
|
|
|
}
|
|
|
|
|
2017-08-18 03:42:20 +03:00
|
|
|
fn contains_roots_of_absolute_flow_tree(&self) -> bool {
|
|
|
|
self.block_flow.contains_roots_of_absolute_flow_tree()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_absolute_containing_block(&self) -> bool {
|
|
|
|
self.block_flow.is_absolute_containing_block()
|
|
|
|
}
|
|
|
|
|
2016-01-29 22:19:14 +03:00
|
|
|
fn generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au> {
|
|
|
|
self.block_flow.generated_containing_block_size(flow)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn iterate_through_fragment_border_boxes(&self,
|
|
|
|
iterator: &mut FragmentBorderBoxIterator,
|
|
|
|
level: i32,
|
|
|
|
stacking_context_position: &Point2D<Au>) {
|
|
|
|
self.block_flow.iterate_through_fragment_border_boxes(iterator, level, stacking_context_position);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) {
|
|
|
|
self.block_flow.mutate_fragments(mutator);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn print_extra_flow_children(&self, print_tree: &mut PrintTree) {
|
|
|
|
self.block_flow.print_extra_flow_children(print_tree);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Flow for MulticolColumnFlow {
|
|
|
|
fn class(&self) -> FlowClass {
|
|
|
|
FlowClass::MulticolColumn
|
|
|
|
}
|
|
|
|
|
2015-09-04 16:59:04 +03:00
|
|
|
fn as_mut_block(&mut self) -> &mut BlockFlow {
|
2015-04-29 03:48:45 +03:00
|
|
|
&mut self.block_flow
|
|
|
|
}
|
|
|
|
|
2015-09-04 16:59:04 +03:00
|
|
|
fn as_block(&self) -> &BlockFlow {
|
2015-07-31 11:10:40 +03:00
|
|
|
&self.block_flow
|
|
|
|
}
|
|
|
|
|
2015-04-29 03:48:45 +03:00
|
|
|
fn bubble_inline_sizes(&mut self) {
|
|
|
|
self.block_flow.bubble_inline_sizes();
|
|
|
|
}
|
|
|
|
|
2017-02-08 04:16:05 +03:00
|
|
|
fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
|
2016-01-29 22:19:14 +03:00
|
|
|
debug!("assign_inline_sizes({}): assigning inline_size for flow", "multicol column");
|
2017-02-08 04:16:05 +03:00
|
|
|
self.block_flow.assign_inline_sizes(layout_context);
|
2015-04-29 03:48:45 +03:00
|
|
|
}
|
|
|
|
|
2017-02-08 04:16:05 +03:00
|
|
|
fn assign_block_size(&mut self, ctx: &LayoutContext) {
|
2016-01-29 22:19:14 +03:00
|
|
|
debug!("assign_block_size: assigning block_size for multicol column");
|
2015-04-29 03:48:45 +03:00
|
|
|
self.block_flow.assign_block_size(ctx);
|
|
|
|
}
|
|
|
|
|
2016-01-29 22:19:14 +03:00
|
|
|
fn fragment(&mut self, layout_context: &LayoutContext,
|
|
|
|
fragmentation_context: Option<FragmentationContext>)
|
2016-11-05 00:53:38 +03:00
|
|
|
-> Option<Arc<Flow>> {
|
2016-01-29 22:19:14 +03:00
|
|
|
Flow::fragment(&mut self.block_flow, layout_context, fragmentation_context)
|
|
|
|
}
|
|
|
|
|
2017-08-08 22:04:23 +03:00
|
|
|
fn compute_stacking_relative_position(&mut self, layout_context: &LayoutContext) {
|
|
|
|
self.block_flow.compute_stacking_relative_position(layout_context)
|
2015-04-29 03:48:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
|
|
|
|
self.block_flow.update_late_computed_inline_position_if_necessary(inline_position)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
|
|
|
|
self.block_flow.update_late_computed_block_position_if_necessary(block_position)
|
|
|
|
}
|
|
|
|
|
2016-03-02 03:51:08 +03:00
|
|
|
fn build_display_list(&mut self, state: &mut DisplayListBuildState) {
|
2016-01-29 22:19:14 +03:00
|
|
|
debug!("build_display_list_multicol column");
|
2016-03-02 03:51:08 +03:00
|
|
|
self.block_flow.build_display_list(state);
|
|
|
|
}
|
|
|
|
|
2017-09-15 22:43:40 +03:00
|
|
|
fn collect_stacking_contexts(&mut self, state: &mut StackingContextCollectionState) {
|
2017-01-10 15:48:31 +03:00
|
|
|
self.block_flow.collect_stacking_contexts(state);
|
2015-04-29 03:48:45 +03:00
|
|
|
}
|
|
|
|
|
2017-07-19 16:03:17 +03:00
|
|
|
fn repair_style(&mut self, new_style: &ServoArc<ComputedValues>) {
|
2015-04-29 03:48:45 +03:00
|
|
|
self.block_flow.repair_style(new_style)
|
|
|
|
}
|
|
|
|
|
2016-02-05 08:28:50 +03:00
|
|
|
fn compute_overflow(&self) -> Overflow {
|
2015-04-29 03:48:45 +03:00
|
|
|
self.block_flow.compute_overflow()
|
|
|
|
}
|
|
|
|
|
2017-08-18 03:42:20 +03:00
|
|
|
fn contains_roots_of_absolute_flow_tree(&self) -> bool {
|
|
|
|
self.block_flow.contains_roots_of_absolute_flow_tree()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_absolute_containing_block(&self) -> bool {
|
|
|
|
self.block_flow.is_absolute_containing_block()
|
|
|
|
}
|
|
|
|
|
2015-05-13 22:27:21 +03:00
|
|
|
fn generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au> {
|
|
|
|
self.block_flow.generated_containing_block_size(flow)
|
2015-04-29 03:48:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fn iterate_through_fragment_border_boxes(&self,
|
|
|
|
iterator: &mut FragmentBorderBoxIterator,
|
2015-08-04 03:39:43 +03:00
|
|
|
level: i32,
|
2015-04-29 03:48:45 +03:00
|
|
|
stacking_context_position: &Point2D<Au>) {
|
2016-01-29 22:19:14 +03:00
|
|
|
self.block_flow.iterate_through_fragment_border_boxes(iterator, level, stacking_context_position);
|
2015-04-29 03:48:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) {
|
2016-01-29 22:19:14 +03:00
|
|
|
self.block_flow.mutate_fragments(mutator);
|
2015-04-29 03:48:45 +03:00
|
|
|
}
|
2015-11-24 05:19:07 +03:00
|
|
|
|
|
|
|
fn print_extra_flow_children(&self, print_tree: &mut PrintTree) {
|
|
|
|
self.block_flow.print_extra_flow_children(print_tree);
|
|
|
|
}
|
2015-04-29 03:48:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Debug for MulticolFlow {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
write!(f, "MulticolFlow: {:?}", self.block_flow)
|
|
|
|
}
|
|
|
|
}
|
2016-01-29 22:19:14 +03:00
|
|
|
|
|
|
|
impl fmt::Debug for MulticolColumnFlow {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
write!(f, "MulticolColumnFlow: {:?}", self.block_flow)
|
|
|
|
}
|
|
|
|
}
|