Remove AttrsList lifetime bound

This commit is contained in:
Jeremy Soller 2022-11-04 09:44:54 -06:00
parent 06cb0c2637
commit ac354c3a2a
No known key found for this signature in database
GPG key ID: 87F211AF2BE4C2FE
6 changed files with 114 additions and 44 deletions

View file

@ -45,7 +45,7 @@ impl StyleSheet for Theme {
} }
pub struct Text { pub struct Text {
line: BufferLine<'static>, line: BufferLine,
metrics: Metrics, metrics: Metrics,
} }
@ -223,25 +223,25 @@ pub fn draw_pixel(
// Do not draw if alpha is zero // Do not draw if alpha is zero
return; return;
} }
if y < 0 || y >= height { if y < 0 || y >= height {
// Skip if y out of bounds // Skip if y out of bounds
return; return;
} }
if x < 0 || x >= width { if x < 0 || x >= width {
// Skip if x out of bounds // Skip if x out of bounds
return; return;
} }
let offset = (y as usize * width as usize + x as usize) * 4; let offset = (y as usize * width as usize + x as usize) * 4;
let mut current = let mut current =
buffer[offset] as u32 | buffer[offset] as u32 |
(buffer[offset + 1] as u32) << 8 | (buffer[offset + 1] as u32) << 8 |
(buffer[offset + 2] as u32) << 16 | (buffer[offset + 2] as u32) << 16 |
(buffer[offset + 3] as u32) << 24; (buffer[offset + 3] as u32) << 24;
if alpha >= 255 || current == 0 { if alpha >= 255 || current == 0 {
// Alpha is 100% or current is null, replace with no blending // Alpha is 100% or current is null, replace with no blending
current = color.0; current = color.0;
@ -253,7 +253,7 @@ pub fn draw_pixel(
+ (alpha * (0x01000000 | ((color.0 & 0x0000FF00) >> 8))); + (alpha * (0x01000000 | ((color.0 & 0x0000FF00) >> 8)));
current = (rb & 0x00FF00FF) | (ag & 0xFF00FF00); current = (rb & 0x00FF00FF) | (ag & 0xFF00FF00);
} }
buffer[offset] = current as u8; buffer[offset] = current as u8;
buffer[offset + 1] = (current >> 8) as u8; buffer[offset + 1] = (current >> 8) as u8;
buffer[offset + 2] = (current >> 16) as u8; buffer[offset + 2] = (current >> 16) as u8;

View file

@ -51,6 +51,40 @@ impl Color {
} }
} }
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum FamilyOwned {
Name(String),
Serif,
SansSerif,
Cursive,
Fantasy,
Monospace,
}
impl FamilyOwned {
pub fn new(family: Family) -> Self {
match family {
Family::Name(name) => FamilyOwned::Name(name.to_string()),
Family::Serif => FamilyOwned::Serif,
Family::SansSerif => FamilyOwned::SansSerif,
Family::Cursive => FamilyOwned::Cursive,
Family::Fantasy => FamilyOwned::Fantasy,
Family::Monospace => FamilyOwned::Monospace,
}
}
pub fn as_family(&self) -> Family {
match self {
FamilyOwned::Name(name) => Family::Name(&name),
FamilyOwned::Serif => Family::Serif,
FamilyOwned::SansSerif => Family::SansSerif,
FamilyOwned::Cursive => Family::Cursive,
FamilyOwned::Fantasy => Family::Fantasy,
FamilyOwned::Monospace => Family::Monospace,
}
}
}
/// Text attributes /// Text attributes
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Attrs<'a> { pub struct Attrs<'a> {
@ -136,30 +170,65 @@ impl<'a> Attrs<'a> {
} }
} }
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct AttrsOwned {
//TODO: should this be an option?
pub color_opt: Option<Color>,
pub family_owned: FamilyOwned,
pub monospaced: bool,
pub stretch: Stretch,
pub style: Style,
pub weight: Weight,
}
impl AttrsOwned {
pub fn new(attrs: Attrs) -> Self {
Self {
color_opt: attrs.color_opt,
family_owned: FamilyOwned::new(attrs.family),
monospaced: attrs.monospaced,
stretch: attrs.stretch,
style: attrs.style,
weight: attrs.weight,
}
}
pub fn as_attrs(&self) -> Attrs {
Attrs {
color_opt: self.color_opt,
family: self.family_owned.as_family(),
monospaced: self.monospaced,
stretch: self.stretch,
style: self.style,
weight: self.weight,
}
}
}
/// List of text attributes to apply to a line /// List of text attributes to apply to a line
//TODO: have this clean up the spans when changes are made //TODO: have this clean up the spans when changes are made
#[derive(Eq, PartialEq)] #[derive(Eq, PartialEq)]
pub struct AttrsList<'a> { pub struct AttrsList {
defaults: Attrs<'a>, defaults: AttrsOwned,
spans: Vec<(Range<usize>, Attrs<'a>)>, spans: Vec<(Range<usize>, AttrsOwned)>,
} }
impl<'a> AttrsList<'a> { impl AttrsList {
/// Create a new attributes list with a set of default [Attrs] /// Create a new attributes list with a set of default [Attrs]
pub fn new(defaults: Attrs<'a>) -> Self { pub fn new(defaults: Attrs) -> Self {
Self { Self {
defaults, defaults: AttrsOwned::new(defaults),
spans: Vec::new(), spans: Vec::new(),
} }
} }
/// Get the default [Attrs] /// Get the default [Attrs]
pub fn defaults(&self) -> Attrs<'a> { pub fn defaults(&self) -> Attrs {
self.defaults self.defaults.as_attrs()
} }
/// Get the current attribute spans /// Get the current attribute spans
pub fn spans(&self) -> &Vec<(Range<usize>, Attrs<'a>)> { pub fn spans(&self) -> &Vec<(Range<usize>, AttrsOwned)> {
&self.spans &self.spans
} }
@ -169,7 +238,7 @@ impl<'a> AttrsList<'a> {
} }
/// Add an attribute span, removes any previous matching parts of spans /// Add an attribute span, removes any previous matching parts of spans
pub fn add_span(&mut self, range: Range<usize>, attrs: Attrs<'a>) { pub fn add_span(&mut self, range: Range<usize>, attrs: Attrs) {
//do not support 1..1 even if by accident. //do not support 1..1 even if by accident.
if range.start == range.end { if range.start == range.end {
return; return;
@ -191,7 +260,7 @@ impl<'a> AttrsList<'a> {
rework_spans.push((rework.0.start..range.start, rework.1)) rework_spans.push((rework.0.start..range.start, rework.1))
} else if self.spans[i].0.end > range.end && self.spans[i].0.start < range.start { } else if self.spans[i].0.end > range.end && self.spans[i].0.start < range.start {
let rework = self.spans.remove(i); let rework = self.spans.remove(i);
rework_spans.push((rework.0.start..range.start, rework.1)); rework_spans.push((rework.0.start..range.start, rework.1.clone()));
rework_spans.push((range.end..rework.0.end, rework.1)); rework_spans.push((range.end..rework.0.end, rework.1));
} else if self.spans[i].0.start > range.end { } else if self.spans[i].0.start > range.end {
break; break;
@ -208,7 +277,7 @@ impl<'a> AttrsList<'a> {
// Combine span if possible // Combine span if possible
let mut combined = false; let mut combined = false;
for span in self.spans.iter_mut() { for span in self.spans.iter_mut() {
if span.1 != attrs { if span.1.as_attrs() != attrs {
// Ignore not matching attrs // Ignore not matching attrs
continue; continue;
} }
@ -230,7 +299,7 @@ impl<'a> AttrsList<'a> {
if ! combined { if ! combined {
//Finally lets add the new span. it should fit now. //Finally lets add the new span. it should fit now.
self.spans.push((range, attrs)); self.spans.push((range, AttrsOwned::new(attrs)));
} }
//sort by start to speed up further additions //sort by start to speed up further additions
@ -240,18 +309,18 @@ impl<'a> AttrsList<'a> {
/// Get the highest priority attribute span for a range /// Get the highest priority attribute span for a range
/// ///
/// This returns the first span that contains the range /// This returns the first span that contains the range
pub fn get_span(&self, range: Range<usize>) -> Attrs<'a> { pub fn get_span(&self, range: Range<usize>) -> Attrs {
for span in self.spans.iter() { for span in self.spans.iter() {
if range.start >= span.0.start && range.end <= span.0.end { if range.start >= span.0.start && range.end <= span.0.end {
return span.1; return span.1.as_attrs();
} }
} }
self.defaults self.defaults.as_attrs()
} }
/// Split attributes list at an offset /// Split attributes list at an offset
pub fn split_off(&mut self, index: usize) -> Self { pub fn split_off(&mut self, index: usize) -> Self {
let mut new = Self::new(self.defaults); let mut new = Self::new(self.defaults.as_attrs());
let mut i = 0; let mut i = 0;
while i < self.spans.len() { while i < self.spans.len() {
if self.spans[i].0.end <= index { if self.spans[i].0.end <= index {
@ -268,7 +337,7 @@ impl<'a> AttrsList<'a> {
// New span has index..end // New span has index..end
new.spans.push(( new.spans.push((
0..self.spans[i].0.end - index, 0..self.spans[i].0.end - index,
self.spans[i].1 self.spans[i].1.clone()
)); ));
// Old span has start..index // Old span has start..index
self.spans[i].0.end = index; self.spans[i].0.end = index;

View file

@ -141,7 +141,7 @@ impl fmt::Display for Metrics {
pub struct Buffer<'a> { pub struct Buffer<'a> {
font_system: &'a FontSystem<'a>, font_system: &'a FontSystem<'a>,
/// Lines (or paragraphs) of text in the buffer /// Lines (or paragraphs) of text in the buffer
pub lines: Vec<BufferLine<'a>>, pub lines: Vec<BufferLine>,
metrics: Metrics, metrics: Metrics,
width: i32, width: i32,
height: i32, height: i32,

View file

@ -1,20 +1,20 @@
use crate::{AttrsList, FontSystem, LayoutLine, ShapeLine}; use crate::{AttrsList, FontSystem, LayoutLine, ShapeLine};
/// A line (or paragraph) of text that is shaped and laid out /// A line (or paragraph) of text that is shaped and laid out
pub struct BufferLine<'a> { pub struct BufferLine {
//TODO: make this not pub(crate) //TODO: make this not pub(crate)
text: String, text: String,
attrs_list: AttrsList<'a>, attrs_list: AttrsList,
wrap_simple: bool, wrap_simple: bool,
shape_opt: Option<ShapeLine>, shape_opt: Option<ShapeLine>,
layout_opt: Option<Vec<LayoutLine>>, layout_opt: Option<Vec<LayoutLine>>,
} }
impl<'a> BufferLine<'a> { impl BufferLine {
/// Create a new line with the given text and attributes list /// Create a new line with the given text and attributes list
/// Cached shaping and layout can be done using the [Self::shape] and /// Cached shaping and layout can be done using the [Self::shape] and
/// [Self::layout] functions /// [Self::layout] functions
pub fn new<T: Into<String>>(text: T, attrs_list: AttrsList<'a>) -> Self { pub fn new<T: Into<String>>(text: T, attrs_list: AttrsList) -> Self {
Self { Self {
text: text.into(), text: text.into(),
attrs_list, attrs_list,
@ -33,7 +33,7 @@ impl<'a> BufferLine<'a> {
/// ///
/// Will reset shape and layout if it differs from current text and attributes list. /// Will reset shape and layout if it differs from current text and attributes list.
/// Returns true if the line was reset /// Returns true if the line was reset
pub fn set_text<T: AsRef<str> + Into<String>>(&mut self, text: T, attrs_list: AttrsList<'a>) -> bool { pub fn set_text<T: AsRef<str> + Into<String>>(&mut self, text: T, attrs_list: AttrsList) -> bool {
if text.as_ref() != &self.text || attrs_list != self.attrs_list { if text.as_ref() != &self.text || attrs_list != self.attrs_list {
self.text = text.into(); self.text = text.into();
self.attrs_list = attrs_list; self.attrs_list = attrs_list;
@ -45,7 +45,7 @@ impl<'a> BufferLine<'a> {
} }
/// Get attributes list /// Get attributes list
pub fn attrs_list(&self) -> &AttrsList<'a> { pub fn attrs_list(&self) -> &AttrsList {
&self.attrs_list &self.attrs_list
} }
@ -53,7 +53,7 @@ impl<'a> BufferLine<'a> {
/// ///
/// Will reset shape and layout if it differs from current attributes list. /// Will reset shape and layout if it differs from current attributes list.
/// Returns true if the line was reset /// Returns true if the line was reset
pub fn set_attrs_list(&mut self, attrs_list: AttrsList<'a>) -> bool { pub fn set_attrs_list(&mut self, attrs_list: AttrsList) -> bool {
if attrs_list != self.attrs_list { if attrs_list != self.attrs_list {
self.attrs_list = attrs_list; self.attrs_list = attrs_list;
self.reset(); self.reset();
@ -97,7 +97,7 @@ impl<'a> BufferLine<'a> {
for (other_range, attrs) in other.attrs_list.spans() { for (other_range, attrs) in other.attrs_list.spans() {
// Add previous attrs spans // Add previous attrs spans
let range = other_range.start + len..other_range.end + len; let range = other_range.start + len..other_range.end + len;
self.attrs_list.add_span(range, *attrs); self.attrs_list.add_span(range, attrs.as_attrs());
} }
self.reset(); self.reset();
@ -132,7 +132,7 @@ impl<'a> BufferLine<'a> {
} }
/// Shape line, will cache results /// Shape line, will cache results
pub fn shape(&mut self, font_system: &'a FontSystem<'a>) -> &ShapeLine { pub fn shape<'a>(&mut self, font_system: &'a FontSystem<'a>) -> &ShapeLine {
if self.shape_opt.is_none() { if self.shape_opt.is_none() {
self.shape_opt = Some(ShapeLine::new(font_system, &self.text, &self.attrs_list)); self.shape_opt = Some(ShapeLine::new(font_system, &self.text, &self.attrs_list));
self.layout_opt = None; self.layout_opt = None;
@ -146,7 +146,7 @@ impl<'a> BufferLine<'a> {
} }
/// Layout line, will cache results /// Layout line, will cache results
pub fn layout(&mut self, font_system: &'a FontSystem<'a>, font_size: i32, width: i32) -> &[LayoutLine] { pub fn layout<'a>(&mut self, font_system: &'a FontSystem<'a>, font_size: i32, width: i32) -> &[LayoutLine] {
if self.layout_opt.is_none() { if self.layout_opt.is_none() {
let wrap_simple = self.wrap_simple; let wrap_simple = self.wrap_simple;
let shape = self.shape(font_system); let shape = self.shape(font_system);

View file

@ -5,14 +5,14 @@ use std::{
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use crate::{Attrs, Font, FontMatches}; use crate::{Attrs, AttrsOwned, Font, FontMatches};
/// Access system fonts /// Access system fonts
pub struct FontSystem<'a> { pub struct FontSystem<'a> {
pub locale: String, pub locale: String,
pub db: fontdb::Database, pub db: fontdb::Database,
pub font_cache: Mutex<HashMap<fontdb::ID, Option<Arc<Font<'a>>>>>, pub font_cache: Mutex<HashMap<fontdb::ID, Option<Arc<Font<'a>>>>>,
pub font_matches_cache: Mutex<HashMap<Attrs<'a>, Arc<FontMatches<'a>>>>, pub font_matches_cache: Mutex<HashMap<AttrsOwned, Arc<FontMatches<'a>>>>,
} }
impl<'a> FontSystem<'a> { impl<'a> FontSystem<'a> {
@ -79,9 +79,10 @@ impl<'a> FontSystem<'a> {
}).clone() }).clone()
} }
pub fn get_font_matches(&'a self, attrs: Attrs<'a>) -> Arc<FontMatches<'a>> { pub fn get_font_matches(&'a self, attrs: Attrs) -> Arc<FontMatches<'a>> {
let mut font_matches_cache = self.font_matches_cache.lock().unwrap(); let mut font_matches_cache = self.font_matches_cache.lock().unwrap();
font_matches_cache.entry(attrs).or_insert_with(|| { //TODO: do not create AttrsOwned unless entry does not already exist
font_matches_cache.entry(AttrsOwned::new(attrs)).or_insert_with(|| {
let now = std::time::Instant::now(); let now = std::time::Instant::now();
let mut fonts = Vec::new(); let mut fonts = Vec::new();

View file

@ -104,7 +104,7 @@ fn shape_fallback(
fn shape_run<'a>( fn shape_run<'a>(
font_system: &'a FontSystem<'a>, font_system: &'a FontSystem<'a>,
line: &str, line: &str,
attrs_list: &AttrsList<'a>, attrs_list: &AttrsList,
start_run: usize, start_run: usize,
end_run: usize, end_run: usize,
span_rtl: bool, span_rtl: bool,
@ -283,7 +283,7 @@ impl ShapeWord {
pub fn new<'a>( pub fn new<'a>(
font_system: &'a FontSystem<'a>, font_system: &'a FontSystem<'a>,
line: &str, line: &str,
attrs_list: &AttrsList<'a>, attrs_list: &AttrsList,
start_word: usize, start_word: usize,
end_word: usize, end_word: usize,
span_rtl: bool, span_rtl: bool,
@ -346,7 +346,7 @@ impl ShapeSpan {
pub fn new<'a>( pub fn new<'a>(
font_system: &'a FontSystem<'a>, font_system: &'a FontSystem<'a>,
line: &str, line: &str,
attrs_list: &AttrsList<'a>, attrs_list: &AttrsList,
start_span: usize, start_span: usize,
end_span: usize, end_span: usize,
line_rtl: bool, line_rtl: bool,
@ -426,7 +426,7 @@ impl ShapeLine {
pub fn new<'a>( pub fn new<'a>(
font_system: &'a FontSystem<'a>, font_system: &'a FontSystem<'a>,
line: &str, line: &str,
attrs_list: &AttrsList<'a> attrs_list: &AttrsList
) -> Self { ) -> Self {
let mut spans = Vec::new(); let mut spans = Vec::new();