mirror of
https://github.com/servo/servo.git
synced 2025-08-01 03:30:33 +01:00
Cargoify servo
This commit is contained in:
parent
db2f642c32
commit
c6ab60dbfc
1761 changed files with 8423 additions and 2294 deletions
439
components/layout/floats.rs
Normal file
439
components/layout/floats.rs
Normal file
|
@ -0,0 +1,439 @@
|
|||
/* 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/. */
|
||||
|
||||
use servo_util::geometry::{Au, max, min};
|
||||
use servo_util::logical_geometry::WritingMode;
|
||||
use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize};
|
||||
use std::i32;
|
||||
use std::fmt;
|
||||
use style::computed_values::float;
|
||||
use sync::Arc;
|
||||
|
||||
/// The kind of float: left or right.
|
||||
#[deriving(Clone, Encodable)]
|
||||
pub enum FloatKind {
|
||||
FloatLeft,
|
||||
FloatRight
|
||||
}
|
||||
|
||||
impl FloatKind {
|
||||
pub fn from_property(property: float::T) -> FloatKind {
|
||||
match property {
|
||||
float::none => fail!("can't create a float type from an unfloated property"),
|
||||
float::left => FloatLeft,
|
||||
float::right => FloatRight,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The kind of clearance: left, right, or both.
|
||||
pub enum ClearType {
|
||||
ClearLeft,
|
||||
ClearRight,
|
||||
ClearBoth,
|
||||
}
|
||||
|
||||
/// Information about a single float.
|
||||
#[deriving(Clone)]
|
||||
struct Float {
|
||||
/// The boundaries of this float.
|
||||
bounds: LogicalRect<Au>,
|
||||
/// The kind of float: left or right.
|
||||
kind: FloatKind,
|
||||
}
|
||||
|
||||
impl fmt::Show for Float {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "bounds={} kind={:?}", self.bounds, self.kind)
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about the floats next to a flow.
|
||||
///
|
||||
/// FIXME(pcwalton): When we have fast `MutexArc`s, try removing `#[deriving(Clone)]` and wrap in a
|
||||
/// mutex.
|
||||
#[deriving(Clone)]
|
||||
struct FloatList {
|
||||
/// Information about each of the floats here.
|
||||
floats: Vec<Float>,
|
||||
/// Cached copy of the maximum block-start offset of the float.
|
||||
max_block_start: Au,
|
||||
}
|
||||
|
||||
impl FloatList {
|
||||
fn new() -> FloatList {
|
||||
FloatList {
|
||||
floats: vec!(),
|
||||
max_block_start: Au(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for FloatList {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "max_block_start={} floats={:?}", self.max_block_start, self.floats)
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps a `FloatList` to avoid allocation in the common case of no floats.
|
||||
///
|
||||
/// FIXME(pcwalton): When we have fast `MutexArc`s, try removing `CowArc` and use a mutex instead.
|
||||
#[deriving(Clone)]
|
||||
struct FloatListRef {
|
||||
list: Option<Arc<FloatList>>,
|
||||
}
|
||||
|
||||
impl FloatListRef {
|
||||
fn new() -> FloatListRef {
|
||||
FloatListRef {
|
||||
list: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the list is allocated and false otherwise. If false, there are guaranteed
|
||||
/// not to be any floats.
|
||||
fn is_present(&self) -> bool {
|
||||
self.list.is_some()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get<'a>(&'a self) -> Option<&'a FloatList> {
|
||||
match self.list {
|
||||
None => None,
|
||||
Some(ref list) => Some(&**list),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(experimental)]
|
||||
#[inline]
|
||||
fn get_mut<'a>(&'a mut self) -> &'a mut FloatList {
|
||||
if self.list.is_none() {
|
||||
self.list = Some(Arc::new(FloatList::new()))
|
||||
}
|
||||
self.list.as_mut().unwrap().make_unique()
|
||||
}
|
||||
}
|
||||
|
||||
/// All the information necessary to place a float.
|
||||
pub struct PlacementInfo {
|
||||
/// The dimensions of the float.
|
||||
pub size: LogicalSize<Au>,
|
||||
/// The minimum block-start of the float, as determined by earlier elements.
|
||||
pub ceiling: Au,
|
||||
/// The maximum inline-end position of the float, generally determined by the containing block.
|
||||
pub max_inline_size: Au,
|
||||
/// The kind of float.
|
||||
pub kind: FloatKind
|
||||
}
|
||||
|
||||
impl fmt::Show for PlacementInfo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "size={} ceiling={} max_inline_size={} kind={:?}", self.size, self.ceiling, self.max_inline_size, self.kind)
|
||||
}
|
||||
}
|
||||
|
||||
fn range_intersect(block_start_1: Au, block_end_1: Au, block_start_2: Au, block_end_2: Au) -> (Au, Au) {
|
||||
(max(block_start_1, block_start_2), min(block_end_1, block_end_2))
|
||||
}
|
||||
|
||||
/// Encapsulates information about floats. This is optimized to avoid allocation if there are
|
||||
/// no floats, and to avoid copying when translating the list of floats downward.
|
||||
#[deriving(Clone)]
|
||||
pub struct Floats {
|
||||
/// The list of floats.
|
||||
list: FloatListRef,
|
||||
/// The offset of the flow relative to the first float.
|
||||
offset: LogicalSize<Au>,
|
||||
pub writing_mode: WritingMode,
|
||||
}
|
||||
|
||||
impl fmt::Show for Floats {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.list.get() {
|
||||
None => {
|
||||
write!(f, "[empty]")
|
||||
}
|
||||
Some(list) => {
|
||||
write!(f, "offset={} floats={}", self.offset, list)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Floats {
|
||||
/// Creates a new `Floats` object.
|
||||
pub fn new(writing_mode: WritingMode) -> Floats {
|
||||
Floats {
|
||||
list: FloatListRef::new(),
|
||||
offset: LogicalSize::zero(writing_mode),
|
||||
writing_mode: writing_mode,
|
||||
}
|
||||
}
|
||||
|
||||
/// Adjusts the recorded offset of the flow relative to the first float.
|
||||
pub fn translate(&mut self, delta: LogicalSize<Au>) {
|
||||
self.offset = self.offset + delta
|
||||
}
|
||||
|
||||
/// Returns the position of the last float in flow coordinates.
|
||||
pub fn last_float_pos(&self) -> Option<LogicalPoint<Au>> {
|
||||
match self.list.get() {
|
||||
None => None,
|
||||
Some(list) => {
|
||||
match list.floats.last() {
|
||||
None => None,
|
||||
Some(float) => Some(float.bounds.start + self.offset),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a rectangle that encloses the region from block-start to block-start + block-size, with inline-size small
|
||||
/// enough that it doesn't collide with any floats. max_x is the x-coordinate beyond which
|
||||
/// floats have no effect. (Generally this is the containing block inline-size.)
|
||||
pub fn available_rect(&self, block_start: Au, block_size: Au, max_x: Au) -> Option<LogicalRect<Au>> {
|
||||
let list = match self.list.get() {
|
||||
None => return None,
|
||||
Some(list) => list,
|
||||
};
|
||||
|
||||
let block_start = block_start - self.offset.block;
|
||||
|
||||
debug!("available_rect: trying to find space at {}", block_start);
|
||||
|
||||
// Relevant dimensions for the inline-end-most inline-start float
|
||||
let mut max_inline_start = Au(0) - self.offset.inline;
|
||||
let mut l_block_start = None;
|
||||
let mut l_block_end = None;
|
||||
// Relevant dimensions for the inline-start-most inline-end float
|
||||
let mut min_inline_end = max_x - self.offset.inline;
|
||||
let mut r_block_start = None;
|
||||
let mut r_block_end = None;
|
||||
|
||||
// Find the float collisions for the given vertical range.
|
||||
for float in list.floats.iter() {
|
||||
debug!("available_rect: Checking for collision against float");
|
||||
let float_pos = float.bounds.start;
|
||||
let float_size = float.bounds.size;
|
||||
|
||||
debug!("float_pos: {}, float_size: {}", float_pos, float_size);
|
||||
match float.kind {
|
||||
FloatLeft if float_pos.i + float_size.inline > max_inline_start &&
|
||||
float_pos.b + float_size.block > block_start && float_pos.b < block_start + block_size => {
|
||||
max_inline_start = float_pos.i + float_size.inline;
|
||||
|
||||
l_block_start = Some(float_pos.b);
|
||||
l_block_end = Some(float_pos.b + float_size.block);
|
||||
|
||||
debug!("available_rect: collision with inline_start float: new max_inline_start is {}",
|
||||
max_inline_start);
|
||||
}
|
||||
FloatRight if float_pos.i < min_inline_end &&
|
||||
float_pos.b + float_size.block > block_start && float_pos.b < block_start + block_size => {
|
||||
min_inline_end = float_pos.i;
|
||||
|
||||
r_block_start = Some(float_pos.b);
|
||||
r_block_end = Some(float_pos.b + float_size.block);
|
||||
debug!("available_rect: collision with inline_end float: new min_inline_end is {}",
|
||||
min_inline_end);
|
||||
}
|
||||
FloatLeft | FloatRight => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Extend the vertical range of the rectangle to the closest floats.
|
||||
// If there are floats on both sides, take the intersection of the
|
||||
// two areas. Also make sure we never return a block-start smaller than the
|
||||
// given upper bound.
|
||||
let (block_start, block_end) = match (r_block_start, r_block_end, l_block_start, l_block_end) {
|
||||
(Some(r_block_start), Some(r_block_end), Some(l_block_start), Some(l_block_end)) =>
|
||||
range_intersect(max(block_start, r_block_start), r_block_end, max(block_start, l_block_start), l_block_end),
|
||||
|
||||
(None, None, Some(l_block_start), Some(l_block_end)) => (max(block_start, l_block_start), l_block_end),
|
||||
(Some(r_block_start), Some(r_block_end), None, None) => (max(block_start, r_block_start), r_block_end),
|
||||
(None, None, None, None) => return None,
|
||||
_ => fail!("Reached unreachable state when computing float area")
|
||||
};
|
||||
|
||||
// FIXME(eatkinson): This assertion is too strong and fails in some cases. It is OK to
|
||||
// return negative inline-sizes since we check against that inline-end away, but we should still
|
||||
// undersrtand why they occur and add a stronger assertion here.
|
||||
// assert!(max_inline-start < min_inline-end);
|
||||
|
||||
assert!(block_start <= block_end, "Float position error");
|
||||
|
||||
Some(LogicalRect::new(
|
||||
self.writing_mode, max_inline_start + self.offset.inline, block_start + self.offset.block,
|
||||
min_inline_end - max_inline_start, block_end - block_start
|
||||
))
|
||||
}
|
||||
|
||||
/// Adds a new float to the list.
|
||||
pub fn add_float(&mut self, info: &PlacementInfo) {
|
||||
let new_info;
|
||||
{
|
||||
let list = self.list.get_mut();
|
||||
new_info = PlacementInfo {
|
||||
size: info.size,
|
||||
ceiling: max(info.ceiling, list.max_block_start + self.offset.block),
|
||||
max_inline_size: info.max_inline_size,
|
||||
kind: info.kind
|
||||
}
|
||||
}
|
||||
|
||||
debug!("add_float: added float with info {:?}", new_info);
|
||||
|
||||
let new_float = Float {
|
||||
bounds: LogicalRect::from_point_size(
|
||||
self.writing_mode,
|
||||
self.place_between_floats(&new_info).start - self.offset,
|
||||
info.size,
|
||||
),
|
||||
kind: info.kind
|
||||
};
|
||||
|
||||
let list = self.list.get_mut();
|
||||
list.floats.push(new_float);
|
||||
list.max_block_start = max(list.max_block_start, new_float.bounds.start.b);
|
||||
}
|
||||
|
||||
/// Given the block-start 3 sides of the rectangle, finds the largest block-size that will result in the
|
||||
/// rectangle not colliding with any floats. Returns None if that block-size is infinite.
|
||||
fn max_block_size_for_bounds(&self, inline_start: Au, block_start: Au, inline_size: Au) -> Option<Au> {
|
||||
let list = match self.list.get() {
|
||||
None => return None,
|
||||
Some(list) => list,
|
||||
};
|
||||
|
||||
let block_start = block_start - self.offset.block;
|
||||
let inline_start = inline_start - self.offset.inline;
|
||||
let mut max_block_size = None;
|
||||
|
||||
for float in list.floats.iter() {
|
||||
if float.bounds.start.b + float.bounds.size.block > block_start &&
|
||||
float.bounds.start.i + float.bounds.size.inline > inline_start &&
|
||||
float.bounds.start.i < inline_start + inline_size {
|
||||
let new_y = float.bounds.start.b;
|
||||
max_block_size = Some(min(max_block_size.unwrap_or(new_y), new_y));
|
||||
}
|
||||
}
|
||||
|
||||
max_block_size.map(|h| h + self.offset.block)
|
||||
}
|
||||
|
||||
/// Given placement information, finds the closest place a fragment can be positioned without
|
||||
/// colliding with any floats.
|
||||
pub fn place_between_floats(&self, info: &PlacementInfo) -> LogicalRect<Au> {
|
||||
debug!("place_between_floats: Placing object with {}", info.size);
|
||||
|
||||
// If no floats, use this fast path.
|
||||
if !self.list.is_present() {
|
||||
match info.kind {
|
||||
FloatLeft => {
|
||||
return LogicalRect::new(
|
||||
self.writing_mode,
|
||||
Au(0),
|
||||
info.ceiling,
|
||||
info.max_inline_size,
|
||||
Au(i32::MAX))
|
||||
}
|
||||
FloatRight => {
|
||||
return LogicalRect::new(
|
||||
self.writing_mode,
|
||||
info.max_inline_size - info.size.inline,
|
||||
info.ceiling,
|
||||
info.max_inline_size,
|
||||
Au(i32::MAX))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Can't go any higher than previous floats or previous elements in the document.
|
||||
let mut float_b = info.ceiling;
|
||||
loop {
|
||||
let maybe_location = self.available_rect(float_b, info.size.block, info.max_inline_size);
|
||||
debug!("place_float: Got available rect: {:?} for y-pos: {}", maybe_location, float_b);
|
||||
match maybe_location {
|
||||
// If there are no floats blocking us, return the current location
|
||||
// TODO(eatkinson): integrate with overflow
|
||||
None => {
|
||||
return match info.kind {
|
||||
FloatLeft => {
|
||||
LogicalRect::new(
|
||||
self.writing_mode,
|
||||
Au(0),
|
||||
float_b,
|
||||
info.max_inline_size,
|
||||
Au(i32::MAX))
|
||||
}
|
||||
FloatRight => {
|
||||
LogicalRect::new(
|
||||
self.writing_mode,
|
||||
info.max_inline_size - info.size.inline,
|
||||
float_b,
|
||||
info.max_inline_size,
|
||||
Au(i32::MAX))
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(rect) => {
|
||||
assert!(rect.start.b + rect.size.block != float_b,
|
||||
"Non-terminating float placement");
|
||||
|
||||
// Place here if there is enough room
|
||||
if rect.size.inline >= info.size.inline {
|
||||
let block_size = self.max_block_size_for_bounds(rect.start.i,
|
||||
rect.start.b,
|
||||
rect.size.inline);
|
||||
let block_size = block_size.unwrap_or(Au(i32::MAX));
|
||||
return match info.kind {
|
||||
FloatLeft => {
|
||||
LogicalRect::new(
|
||||
self.writing_mode,
|
||||
rect.start.i,
|
||||
float_b,
|
||||
rect.size.inline,
|
||||
block_size)
|
||||
}
|
||||
FloatRight => {
|
||||
LogicalRect::new(
|
||||
self.writing_mode,
|
||||
rect.start.i + rect.size.inline - info.size.inline,
|
||||
float_b,
|
||||
rect.size.inline,
|
||||
block_size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to place at the next-lowest location.
|
||||
// Need to be careful of fencepost errors.
|
||||
float_b = rect.start.b + rect.size.block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clearance(&self, clear: ClearType) -> Au {
|
||||
let list = match self.list.get() {
|
||||
None => return Au(0),
|
||||
Some(list) => list,
|
||||
};
|
||||
|
||||
let mut clearance = Au(0);
|
||||
for float in list.floats.iter() {
|
||||
match (clear, float.kind) {
|
||||
(ClearLeft, FloatLeft) |
|
||||
(ClearRight, FloatRight) |
|
||||
(ClearBoth, _) => {
|
||||
let b = self.offset.block + float.bounds.start.b + float.bounds.size.block;
|
||||
clearance = max(clearance, b);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
clearance
|
||||
}
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue