diff options
| author | Martin Haug <mhaug@live.de> | 2022-06-04 12:57:45 +0200 |
|---|---|---|
| committer | Martin Haug <mhaug@live.de> | 2022-06-04 12:57:45 +0200 |
| commit | 4640585fbdf72df993dbed46799844aa78996cce (patch) | |
| tree | 38a09389885a61068970441d6d27178a2ae4f115 /src/frame.rs | |
| parent | a937462491a63f5cff3551b5bb8bc45fb350f0b6 (diff) | |
First iteration of outline items
Diffstat (limited to 'src/frame.rs')
| -rw-r--r-- | src/frame.rs | 99 |
1 files changed, 96 insertions, 3 deletions
diff --git a/src/frame.rs b/src/frame.rs index 1bd1f454..8b14b2b1 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -22,6 +22,8 @@ pub struct Frame { pub baseline: Option<Length>, /// The elements composing this layout. pub elements: Vec<(Point, Element)>, + /// The semantic role of the frame. + role: Option<Role>, } impl Frame { @@ -29,7 +31,12 @@ impl Frame { #[track_caller] pub fn new(size: Size) -> Self { assert!(size.is_finite()); - Self { size, baseline: None, elements: vec![] } + Self { + size, + baseline: None, + elements: vec![], + role: None, + } } /// The baseline of the frame. @@ -43,6 +50,11 @@ impl Frame { self.elements.len() } + /// The role of the frame. + pub fn role(&self) -> Option<Role> { + self.role + } + /// Whether the frame has comparatively few elements. pub fn is_light(&self) -> bool { self.elements.len() <= 5 @@ -58,7 +70,12 @@ impl Frame { /// Automatically decides whether to inline the frame or to include it as a /// group based on the number of elements in the frame. pub fn push_frame(&mut self, pos: Point, frame: impl FrameRepr) { - if self.elements.is_empty() || frame.as_ref().is_light() { + if (self.elements.is_empty() || frame.as_ref().is_light()) + && (frame.as_ref().role().is_none() || self.role.is_none()) + { + if self.role.is_none() { + self.role = frame.as_ref().role() + } frame.inline(self, self.layer(), pos); } else { self.elements.push((pos, Element::Group(Group::new(frame.share())))); @@ -80,7 +97,12 @@ impl Frame { /// Add a frame at a position in the background. pub fn prepend_frame(&mut self, pos: Point, frame: impl FrameRepr) { - if self.elements.is_empty() || frame.as_ref().is_light() { + if (self.elements.is_empty() || frame.as_ref().is_light()) + && (frame.as_ref().role().is_none() || self.role.is_none()) + { + if self.role.is_none() { + self.role = frame.as_ref().role() + } frame.inline(self, 0, pos); } else { self.elements @@ -125,6 +147,15 @@ impl Frame { self.group(|g| g.transform = transform); } + /// Apply the given role to the frame if it doesn't already have one. + pub fn apply_role(&mut self, role: Role) { + match self.role { + None => self.role = Some(role), + Some(old) if old.is_weak() => self.role = Some(role), + Some(_) => {} + } + } + /// Clip the contents of a frame to its size. pub fn clip(&mut self) { self.group(|g| g.clips = true); @@ -146,10 +177,26 @@ impl Frame { pub fn link(&mut self, dest: Destination) { self.push(Point::zero(), Element::Link(dest, self.size)); } + + /// Recover the text inside of the frame and its children. + pub fn inner_text(&self) -> EcoString { + let mut res = EcoString::new(); + for (_, element) in &self.elements { + match element { + Element::Text(text) => res.push_str( + &text.glyphs.iter().map(|glyph| glyph.c).collect::<EcoString>(), + ), + Element::Group(group) => res.push_str(&group.frame.inner_text()), + _ => {} + } + } + res + } } impl Debug for Frame { fn fmt(&self, f: &mut Formatter) -> fmt::Result { + self.role.fmt(f)?; f.debug_list() .entries(self.elements.iter().map(|(_, element)| element)) .finish() @@ -362,3 +409,49 @@ impl Location { } } } + +/// A semantic role of a frame. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum Role { + /// A paragraph. + Paragraph, + /// A heading with some level. + Heading(usize), + /// A generic block-level subdivision. + GenericBlock, + /// A generic inline subdivision. + GenericInline, + /// A list. The boolean indicates whether it is ordered. + List(bool), + /// A list item. Must have a list parent. + ListItem, + /// The label of a list item. + ListLabel, + /// The body of a list item. + ListItemBody, + /// A mathematical formula. + Formula, + /// A table. + Table, + /// A table row. + TableRow, + /// A table cell. + TableCell, + /// A code fragment. + Code, + /// A page header. + Header, + /// A page footer. + Footer, + /// A page background. + Background, +} + +impl Role { + fn is_weak(&self) -> bool { + match self { + Self::Paragraph | Self::GenericBlock | Self::GenericInline => true, + _ => false, + } + } +} |
