summaryrefslogtreecommitdiff
path: root/src/frame.rs
diff options
context:
space:
mode:
authorMartin Haug <mhaug@live.de>2022-06-04 12:57:45 +0200
committerMartin Haug <mhaug@live.de>2022-06-04 12:57:45 +0200
commit4640585fbdf72df993dbed46799844aa78996cce (patch)
tree38a09389885a61068970441d6d27178a2ae4f115 /src/frame.rs
parenta937462491a63f5cff3551b5bb8bc45fb350f0b6 (diff)
First iteration of outline items
Diffstat (limited to 'src/frame.rs')
-rw-r--r--src/frame.rs99
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,
+ }
+ }
+}