Merge pull request #371 from StewartCanva/stewart-add-kerning-ligatures
Add support for kerning and ligature font features
This commit is contained in:
commit
7b79d720cf
15 changed files with 221 additions and 107 deletions
|
|
@ -31,7 +31,7 @@ fn main() {
|
|||
|
||||
let attrs = Attrs::new().family(Family::Monospace);
|
||||
match fs::read_to_string(&path) {
|
||||
Ok(text) => buffer.set_text(&text, attrs, Shaping::Advanced),
|
||||
Ok(text) => buffer.set_text(&text, &attrs, Shaping::Advanced),
|
||||
Err(err) => {
|
||||
log::error!("failed to load {:?}: {}", path, err);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,89 +21,112 @@ use winit::{
|
|||
|
||||
fn set_buffer_text(buffer: &mut BorrowedWithFontSystem<'_, Buffer>) {
|
||||
let attrs = Attrs::new();
|
||||
let serif_attrs = attrs.family(Family::Serif);
|
||||
let mono_attrs = attrs.family(Family::Monospace);
|
||||
let comic_attrs = attrs.family(Family::Name("Comic Neue"));
|
||||
let serif_attrs = attrs.clone().family(Family::Serif);
|
||||
let mono_attrs = attrs.clone().family(Family::Monospace);
|
||||
let comic_attrs = attrs.clone().family(Family::Name("Comic Neue"));
|
||||
|
||||
let spans: &[(&str, Attrs)] = &[
|
||||
("Font size 64 ", attrs.metrics(Metrics::relative(64.0, 1.2))),
|
||||
("Font size 8 ", attrs.metrics(Metrics::relative(8.0, 1.2))),
|
||||
("Font size 20 ", attrs.metrics(Metrics::relative(20.0, 1.2))),
|
||||
("Font size 14 ", attrs.metrics(Metrics::relative(14.0, 1.2))),
|
||||
(
|
||||
"Font size 64 ",
|
||||
attrs.clone().metrics(Metrics::relative(64.0, 1.2)),
|
||||
),
|
||||
(
|
||||
"Font size 8 ",
|
||||
attrs.clone().metrics(Metrics::relative(8.0, 1.2)),
|
||||
),
|
||||
(
|
||||
"Font size 20 ",
|
||||
attrs.clone().metrics(Metrics::relative(20.0, 1.2)),
|
||||
),
|
||||
(
|
||||
"Font size 14 ",
|
||||
attrs.clone().metrics(Metrics::relative(14.0, 1.2)),
|
||||
),
|
||||
(
|
||||
"Font size 48\n",
|
||||
attrs.metrics(Metrics::relative(48.0, 1.2)),
|
||||
attrs.clone().metrics(Metrics::relative(48.0, 1.2)),
|
||||
),
|
||||
("B", attrs.weight(Weight::BOLD)),
|
||||
("old ", attrs),
|
||||
("I", attrs.style(Style::Italic)),
|
||||
("talic ", attrs),
|
||||
("f", attrs),
|
||||
("i ", attrs),
|
||||
("f", attrs.weight(Weight::BOLD)),
|
||||
("i ", attrs),
|
||||
("f", attrs.style(Style::Italic)),
|
||||
("i \n", attrs),
|
||||
("Sans-Serif Normal ", attrs),
|
||||
("Sans-Serif Bold ", attrs.weight(Weight::BOLD)),
|
||||
("Sans-Serif Italic ", attrs.style(Style::Italic)),
|
||||
("B", attrs.clone().weight(Weight::BOLD)),
|
||||
("old ", attrs.clone()),
|
||||
("I", attrs.clone().style(Style::Italic)),
|
||||
("talic ", attrs.clone()),
|
||||
("f", attrs.clone()),
|
||||
("i ", attrs.clone()),
|
||||
("f", attrs.clone().weight(Weight::BOLD)),
|
||||
("i ", attrs.clone()),
|
||||
("f", attrs.clone().style(Style::Italic)),
|
||||
("i \n", attrs.clone()),
|
||||
("Sans-Serif Normal ", attrs.clone()),
|
||||
("Sans-Serif Bold ", attrs.clone().weight(Weight::BOLD)),
|
||||
("Sans-Serif Italic ", attrs.clone().style(Style::Italic)),
|
||||
(
|
||||
"Sans-Serif Fake Italic ",
|
||||
attrs.cache_key_flags(CacheKeyFlags::FAKE_ITALIC),
|
||||
attrs.clone().cache_key_flags(CacheKeyFlags::FAKE_ITALIC),
|
||||
),
|
||||
(
|
||||
"Sans-Serif Bold Italic\n",
|
||||
attrs.weight(Weight::BOLD).style(Style::Italic),
|
||||
attrs.clone().weight(Weight::BOLD).style(Style::Italic),
|
||||
),
|
||||
("Serif Normal ", serif_attrs),
|
||||
("Serif Bold ", serif_attrs.weight(Weight::BOLD)),
|
||||
("Serif Italic ", serif_attrs.style(Style::Italic)),
|
||||
("Serif Normal ", serif_attrs.clone()),
|
||||
("Serif Bold ", serif_attrs.clone().weight(Weight::BOLD)),
|
||||
("Serif Italic ", serif_attrs.clone().style(Style::Italic)),
|
||||
(
|
||||
"Serif Bold Italic\n",
|
||||
serif_attrs.weight(Weight::BOLD).style(Style::Italic),
|
||||
serif_attrs
|
||||
.clone()
|
||||
.weight(Weight::BOLD)
|
||||
.style(Style::Italic),
|
||||
),
|
||||
("Mono Normal ", mono_attrs),
|
||||
("Mono Bold ", mono_attrs.weight(Weight::BOLD)),
|
||||
("Mono Italic ", mono_attrs.style(Style::Italic)),
|
||||
("Mono Normal ", mono_attrs.clone()),
|
||||
("Mono Bold ", mono_attrs.clone().weight(Weight::BOLD)),
|
||||
("Mono Italic ", mono_attrs.clone().style(Style::Italic)),
|
||||
(
|
||||
"Mono Bold Italic\n",
|
||||
mono_attrs.weight(Weight::BOLD).style(Style::Italic),
|
||||
mono_attrs.clone().weight(Weight::BOLD).style(Style::Italic),
|
||||
),
|
||||
("Comic Normal ", comic_attrs),
|
||||
("Comic Bold ", comic_attrs.weight(Weight::BOLD)),
|
||||
("Comic Italic ", comic_attrs.style(Style::Italic)),
|
||||
("Comic Normal ", comic_attrs.clone()),
|
||||
("Comic Bold ", comic_attrs.clone().weight(Weight::BOLD)),
|
||||
("Comic Italic ", comic_attrs.clone().style(Style::Italic)),
|
||||
(
|
||||
"Comic Bold Italic\n",
|
||||
comic_attrs.weight(Weight::BOLD).style(Style::Italic),
|
||||
comic_attrs
|
||||
.clone()
|
||||
.weight(Weight::BOLD)
|
||||
.style(Style::Italic),
|
||||
),
|
||||
("R", attrs.color(Color::rgb(0xFF, 0x00, 0x00))),
|
||||
("A", attrs.color(Color::rgb(0xFF, 0x7F, 0x00))),
|
||||
("I", attrs.color(Color::rgb(0xFF, 0xFF, 0x00))),
|
||||
("N", attrs.color(Color::rgb(0x00, 0xFF, 0x00))),
|
||||
("B", attrs.color(Color::rgb(0x00, 0x00, 0xFF))),
|
||||
("O", attrs.color(Color::rgb(0x4B, 0x00, 0x82))),
|
||||
("W ", attrs.color(Color::rgb(0x94, 0x00, 0xD3))),
|
||||
("Red ", attrs.color(Color::rgb(0xFF, 0x00, 0x00))),
|
||||
("Orange ", attrs.color(Color::rgb(0xFF, 0x7F, 0x00))),
|
||||
("Yellow ", attrs.color(Color::rgb(0xFF, 0xFF, 0x00))),
|
||||
("Green ", attrs.color(Color::rgb(0x00, 0xFF, 0x00))),
|
||||
("Blue ", attrs.color(Color::rgb(0x00, 0x00, 0xFF))),
|
||||
("Indigo ", attrs.color(Color::rgb(0x4B, 0x00, 0x82))),
|
||||
("Violet ", attrs.color(Color::rgb(0x94, 0x00, 0xD3))),
|
||||
("U", attrs.color(Color::rgb(0x94, 0x00, 0xD3))),
|
||||
("N", attrs.color(Color::rgb(0x4B, 0x00, 0x82))),
|
||||
("I", attrs.color(Color::rgb(0x00, 0x00, 0xFF))),
|
||||
("C", attrs.color(Color::rgb(0x00, 0xFF, 0x00))),
|
||||
("O", attrs.color(Color::rgb(0xFF, 0xFF, 0x00))),
|
||||
("R", attrs.color(Color::rgb(0xFF, 0x7F, 0x00))),
|
||||
("N\n", attrs.color(Color::rgb(0xFF, 0x00, 0x00))),
|
||||
("R", attrs.clone().color(Color::rgb(0xFF, 0x00, 0x00))),
|
||||
("A", attrs.clone().color(Color::rgb(0xFF, 0x7F, 0x00))),
|
||||
("I", attrs.clone().color(Color::rgb(0xFF, 0xFF, 0x00))),
|
||||
("N", attrs.clone().color(Color::rgb(0x00, 0xFF, 0x00))),
|
||||
("B", attrs.clone().color(Color::rgb(0x00, 0x00, 0xFF))),
|
||||
("O", attrs.clone().color(Color::rgb(0x4B, 0x00, 0x82))),
|
||||
("W ", attrs.clone().color(Color::rgb(0x94, 0x00, 0xD3))),
|
||||
("Red ", attrs.clone().color(Color::rgb(0xFF, 0x00, 0x00))),
|
||||
("Orange ", attrs.clone().color(Color::rgb(0xFF, 0x7F, 0x00))),
|
||||
("Yellow ", attrs.clone().color(Color::rgb(0xFF, 0xFF, 0x00))),
|
||||
("Green ", attrs.clone().color(Color::rgb(0x00, 0xFF, 0x00))),
|
||||
("Blue ", attrs.clone().color(Color::rgb(0x00, 0x00, 0xFF))),
|
||||
("Indigo ", attrs.clone().color(Color::rgb(0x4B, 0x00, 0x82))),
|
||||
("Violet ", attrs.clone().color(Color::rgb(0x94, 0x00, 0xD3))),
|
||||
("U", attrs.clone().color(Color::rgb(0x94, 0x00, 0xD3))),
|
||||
("N", attrs.clone().color(Color::rgb(0x4B, 0x00, 0x82))),
|
||||
("I", attrs.clone().color(Color::rgb(0x00, 0x00, 0xFF))),
|
||||
("C", attrs.clone().color(Color::rgb(0x00, 0xFF, 0x00))),
|
||||
("O", attrs.clone().color(Color::rgb(0xFF, 0xFF, 0x00))),
|
||||
("R", attrs.clone().color(Color::rgb(0xFF, 0x7F, 0x00))),
|
||||
("N\n", attrs.clone().color(Color::rgb(0xFF, 0x00, 0x00))),
|
||||
(
|
||||
"生活,삶,जिंदगी 😀 FPS\n",
|
||||
attrs.color(Color::rgb(0xFF, 0x00, 0x00)),
|
||||
attrs.clone().color(Color::rgb(0xFF, 0x00, 0x00)),
|
||||
),
|
||||
];
|
||||
|
||||
buffer.set_rich_text(spans.iter().copied(), attrs, Shaping::Advanced, None);
|
||||
buffer.set_rich_text(
|
||||
spans.iter().map(|(text, attrs)| (*text, attrs.clone())),
|
||||
&attrs,
|
||||
Shaping::Advanced,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ fn main() {
|
|||
let text = std::env::args()
|
||||
.nth(1)
|
||||
.unwrap_or(" Hi, Rust! 🦀 ".to_string());
|
||||
buffer.set_text(&text, attrs, Shaping::Advanced);
|
||||
buffer.set_text(&text, &attrs, Shaping::Advanced);
|
||||
|
||||
// Perform shaping as desired
|
||||
buffer.shape_until_scroll(true);
|
||||
|
|
|
|||
93
src/attrs.rs
93
src/attrs.rs
|
|
@ -125,6 +125,72 @@ impl From<CacheMetrics> for Metrics {
|
|||
}
|
||||
}
|
||||
}
|
||||
/// A 4-byte `OpenType` feature tag identifier
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct FeatureTag([u8; 4]);
|
||||
|
||||
impl FeatureTag {
|
||||
pub const fn new(tag: &[u8; 4]) -> Self {
|
||||
Self(*tag)
|
||||
}
|
||||
|
||||
/// Kerning adjusts spacing between specific character pairs
|
||||
pub const KERNING: Self = Self::new(b"kern");
|
||||
/// Standard ligatures (fi, fl, etc.)
|
||||
pub const STANDARD_LIGATURES: Self = Self::new(b"liga");
|
||||
/// Contextual ligatures (context-dependent ligatures)
|
||||
pub const CONTEXTUAL_LIGATURES: Self = Self::new(b"clig");
|
||||
/// Contextual alternates (glyph substitutions based on context)
|
||||
pub const CONTEXTUAL_ALTERNATES: Self = Self::new(b"calt");
|
||||
/// Discretionary ligatures (optional stylistic ligatures)
|
||||
pub const DISCRETIONARY_LIGATURES: Self = Self::new(b"dlig");
|
||||
/// Small caps (lowercase to small capitals)
|
||||
pub const SMALL_CAPS: Self = Self::new(b"smcp");
|
||||
/// All small caps (uppercase and lowercase to small capitals)
|
||||
pub const ALL_SMALL_CAPS: Self = Self::new(b"c2sc");
|
||||
/// Stylistic Set 1 (font-specific alternate glyphs)
|
||||
pub const STYLISTIC_SET_1: Self = Self::new(b"ss01");
|
||||
/// Stylistic Set 2 (font-specific alternate glyphs)
|
||||
pub const STYLISTIC_SET_2: Self = Self::new(b"ss02");
|
||||
|
||||
pub fn as_bytes(&self) -> &[u8; 4] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct Feature {
|
||||
pub tag: FeatureTag,
|
||||
pub value: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
|
||||
pub struct FontFeatures {
|
||||
pub features: Vec<Feature>,
|
||||
}
|
||||
|
||||
impl FontFeatures {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
features: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(&mut self, tag: FeatureTag, value: u32) -> &mut Self {
|
||||
self.features.push(Feature { tag, value });
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable a feature (set to 1)
|
||||
pub fn enable(&mut self, tag: FeatureTag) -> &mut Self {
|
||||
self.set(tag, 1)
|
||||
}
|
||||
|
||||
/// Disable a feature (set to 0)
|
||||
pub fn disable(&mut self, tag: FeatureTag) -> &mut Self {
|
||||
self.set(tag, 0)
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper for letter spacing to get around that f32 doesn't implement Eq and Hash
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
|
@ -158,7 +224,7 @@ impl Hash for LetterSpacing {
|
|||
}
|
||||
|
||||
/// Text attributes
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct Attrs<'a> {
|
||||
//TODO: should this be an option?
|
||||
pub color_opt: Option<Color>,
|
||||
|
|
@ -171,6 +237,7 @@ pub struct Attrs<'a> {
|
|||
pub metrics_opt: Option<CacheMetrics>,
|
||||
/// Letter spacing (tracking) in EM
|
||||
pub letter_spacing_opt: Option<LetterSpacing>,
|
||||
pub font_features: FontFeatures,
|
||||
}
|
||||
|
||||
impl<'a> Attrs<'a> {
|
||||
|
|
@ -188,6 +255,7 @@ impl<'a> Attrs<'a> {
|
|||
cache_key_flags: CacheKeyFlags::empty(),
|
||||
metrics_opt: None,
|
||||
letter_spacing_opt: None,
|
||||
font_features: FontFeatures::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -245,6 +313,12 @@ impl<'a> Attrs<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Set [`FontFeatures`]
|
||||
pub fn font_features(mut self, font_features: FontFeatures) -> Self {
|
||||
self.font_features = font_features;
|
||||
self
|
||||
}
|
||||
|
||||
/// Check if font matches
|
||||
pub fn matches(&self, face: &fontdb::FaceInfo) -> bool {
|
||||
//TODO: smarter way of including emoji
|
||||
|
|
@ -270,8 +344,8 @@ pub struct FontMatchAttrs {
|
|||
weight: Weight,
|
||||
}
|
||||
|
||||
impl<'a> From<Attrs<'a>> for FontMatchAttrs {
|
||||
fn from(attrs: Attrs<'a>) -> Self {
|
||||
impl<'a> From<&Attrs<'a>> for FontMatchAttrs {
|
||||
fn from(attrs: &Attrs<'a>) -> Self {
|
||||
Self {
|
||||
family: FamilyOwned::new(attrs.family),
|
||||
stretch: attrs.stretch,
|
||||
|
|
@ -295,10 +369,11 @@ pub struct AttrsOwned {
|
|||
pub metrics_opt: Option<CacheMetrics>,
|
||||
/// Letter spacing (tracking) in EM
|
||||
pub letter_spacing_opt: Option<LetterSpacing>,
|
||||
pub font_features: FontFeatures,
|
||||
}
|
||||
|
||||
impl AttrsOwned {
|
||||
pub fn new(attrs: Attrs) -> Self {
|
||||
pub fn new(attrs: &Attrs) -> Self {
|
||||
Self {
|
||||
color_opt: attrs.color_opt,
|
||||
family_owned: FamilyOwned::new(attrs.family),
|
||||
|
|
@ -309,6 +384,7 @@ impl AttrsOwned {
|
|||
cache_key_flags: attrs.cache_key_flags,
|
||||
metrics_opt: attrs.metrics_opt,
|
||||
letter_spacing_opt: attrs.letter_spacing_opt,
|
||||
font_features: attrs.font_features.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -323,6 +399,7 @@ impl AttrsOwned {
|
|||
cache_key_flags: self.cache_key_flags,
|
||||
metrics_opt: self.metrics_opt,
|
||||
letter_spacing_opt: self.letter_spacing_opt,
|
||||
font_features: self.font_features.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -337,7 +414,7 @@ pub struct AttrsList {
|
|||
|
||||
impl AttrsList {
|
||||
/// Create a new attributes list with a set of default [Attrs]
|
||||
pub fn new(defaults: Attrs) -> Self {
|
||||
pub fn new(defaults: &Attrs) -> Self {
|
||||
Self {
|
||||
defaults: AttrsOwned::new(defaults),
|
||||
spans: RangeMap::new(),
|
||||
|
|
@ -365,7 +442,7 @@ impl AttrsList {
|
|||
}
|
||||
|
||||
/// Add an attribute span, removes any previous matching parts of spans
|
||||
pub fn add_span(&mut self, range: Range<usize>, attrs: Attrs) {
|
||||
pub fn add_span(&mut self, range: Range<usize>, attrs: &Attrs) {
|
||||
//do not support 1..1 or 2..1 even if by accident.
|
||||
if range.is_empty() {
|
||||
return;
|
||||
|
|
@ -387,7 +464,7 @@ impl AttrsList {
|
|||
/// Split attributes list at an offset
|
||||
#[allow(clippy::missing_panics_doc)]
|
||||
pub fn split_off(&mut self, index: usize) -> Self {
|
||||
let mut new = Self::new(self.defaults.as_attrs());
|
||||
let mut new = Self::new(&self.defaults.as_attrs());
|
||||
let mut removes = Vec::new();
|
||||
|
||||
//get the keys we need to remove or fix.
|
||||
|
|
@ -421,7 +498,7 @@ impl AttrsList {
|
|||
}
|
||||
|
||||
/// Resets the attributes with new defaults.
|
||||
pub(crate) fn reset(mut self, default: Attrs) -> Self {
|
||||
pub(crate) fn reset(mut self, default: &Attrs) -> Self {
|
||||
self.defaults = AttrsOwned::new(default);
|
||||
self.spans.clear();
|
||||
self
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ impl Buffer {
|
|||
/// Will panic if `metrics.line_height` is zero.
|
||||
pub fn new(font_system: &mut FontSystem, metrics: Metrics) -> Self {
|
||||
let mut buffer = Self::new_empty(metrics);
|
||||
buffer.set_text(font_system, "", Attrs::new(), Shaping::Advanced);
|
||||
buffer.set_text(font_system, "", &Attrs::new(), Shaping::Advanced);
|
||||
buffer
|
||||
}
|
||||
|
||||
|
|
@ -676,7 +676,7 @@ impl Buffer {
|
|||
&mut self,
|
||||
font_system: &mut FontSystem,
|
||||
text: &str,
|
||||
attrs: Attrs,
|
||||
attrs: &Attrs,
|
||||
shaping: Shaping,
|
||||
) {
|
||||
self.lines.clear();
|
||||
|
|
@ -710,10 +710,10 @@ impl Buffer {
|
|||
/// buffer.set_rich_text(
|
||||
/// &mut font_system,
|
||||
/// [
|
||||
/// ("hello, ", attrs),
|
||||
/// ("cosmic\ntext", attrs.family(Family::Monospace)),
|
||||
/// ("hello, ", attrs.clone()),
|
||||
/// ("cosmic\ntext", attrs.clone().family(Family::Monospace)),
|
||||
/// ],
|
||||
/// attrs,
|
||||
/// &attrs,
|
||||
/// Shaping::Advanced,
|
||||
/// None,
|
||||
/// );
|
||||
|
|
@ -722,7 +722,7 @@ impl Buffer {
|
|||
&mut self,
|
||||
font_system: &mut FontSystem,
|
||||
spans: I,
|
||||
default_attrs: Attrs,
|
||||
default_attrs: &Attrs,
|
||||
shaping: Shaping,
|
||||
alignment: Option<Align>,
|
||||
) where
|
||||
|
|
@ -758,7 +758,7 @@ impl Buffer {
|
|||
.lines
|
||||
.get_mut(line_count)
|
||||
.map(BufferLine::reclaim_attrs)
|
||||
.unwrap_or_else(|| AttrsList::new(Attrs::new()))
|
||||
.unwrap_or_else(|| AttrsList::new(&Attrs::new()))
|
||||
.reset(default_attrs);
|
||||
let mut line_string = self
|
||||
.lines
|
||||
|
|
@ -792,7 +792,7 @@ impl Buffer {
|
|||
let text_end = line_string.len();
|
||||
// Only add attrs if they don't match the defaults
|
||||
if *attrs != attrs_list.defaults() {
|
||||
attrs_list.add_span(text_start..text_end, *attrs);
|
||||
attrs_list.add_span(text_start..text_end, attrs);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -811,7 +811,7 @@ impl Buffer {
|
|||
.lines
|
||||
.get_mut(line_count + 1)
|
||||
.map(BufferLine::reclaim_attrs)
|
||||
.unwrap_or_else(|| AttrsList::new(Attrs::new()))
|
||||
.unwrap_or_else(|| AttrsList::new(&Attrs::new()))
|
||||
.reset(default_attrs);
|
||||
let next_line_string = self
|
||||
.lines
|
||||
|
|
@ -1428,7 +1428,7 @@ impl BorrowedWithFontSystem<'_, Buffer> {
|
|||
}
|
||||
|
||||
/// Set text of buffer, using provided attributes for each line by default
|
||||
pub fn set_text(&mut self, text: &str, attrs: Attrs, shaping: Shaping) {
|
||||
pub fn set_text(&mut self, text: &str, attrs: &Attrs, shaping: Shaping) {
|
||||
self.inner.set_text(self.font_system, text, attrs, shaping);
|
||||
}
|
||||
|
||||
|
|
@ -1438,14 +1438,14 @@ impl BorrowedWithFontSystem<'_, Buffer> {
|
|||
/// # use cosmic_text::{Attrs, Buffer, Family, FontSystem, Metrics, Shaping};
|
||||
/// # let mut font_system = FontSystem::new();
|
||||
/// let mut buffer = Buffer::new_empty(Metrics::new(32.0, 44.0));
|
||||
/// let mut buffer = buffer.borrow_with(&mut font_system);
|
||||
/// let attrs = Attrs::new().family(Family::Serif);
|
||||
/// buffer.set_rich_text(
|
||||
/// &mut font_system,
|
||||
/// [
|
||||
/// ("hello, ", attrs),
|
||||
/// ("cosmic\ntext", attrs.family(Family::Monospace)),
|
||||
/// ("hello, ", attrs.clone()),
|
||||
/// ("cosmic\ntext", attrs.clone().family(Family::Monospace)),
|
||||
/// ],
|
||||
/// attrs,
|
||||
/// &attrs,
|
||||
/// Shaping::Advanced,
|
||||
/// None,
|
||||
/// );
|
||||
|
|
@ -1453,7 +1453,7 @@ impl BorrowedWithFontSystem<'_, Buffer> {
|
|||
pub fn set_rich_text<'r, 's, I>(
|
||||
&mut self,
|
||||
spans: I,
|
||||
default_attrs: Attrs,
|
||||
default_attrs: &Attrs,
|
||||
shaping: Shaping,
|
||||
alignment: Option<Align>,
|
||||
) where
|
||||
|
|
|
|||
|
|
@ -162,13 +162,13 @@ impl BufferLine {
|
|||
if other.attrs_list.defaults() != self.attrs_list.defaults() {
|
||||
// If default formatting does not match, make a new span for it
|
||||
self.attrs_list
|
||||
.add_span(len..len + other.text().len(), other.attrs_list.defaults());
|
||||
.add_span(len..len + other.text().len(), &other.attrs_list.defaults());
|
||||
}
|
||||
|
||||
for (other_range, attrs) in other.attrs_list.spans_iter() {
|
||||
// Add previous attrs spans
|
||||
let range = other_range.start + len..other_range.end + len;
|
||||
self.attrs_list.add_span(range, attrs.as_attrs());
|
||||
self.attrs_list.add_span(range, &attrs.as_attrs());
|
||||
}
|
||||
|
||||
self.reset();
|
||||
|
|
@ -283,7 +283,7 @@ impl BufferLine {
|
|||
Self {
|
||||
text: String::default(),
|
||||
ending: LineEnding::default(),
|
||||
attrs_list: AttrsList::new(Attrs::new()),
|
||||
attrs_list: AttrsList::new(&Attrs::new()),
|
||||
align: None,
|
||||
shape_opt: Cached::Empty,
|
||||
layout_opt: Cached::Empty,
|
||||
|
|
@ -296,7 +296,7 @@ impl BufferLine {
|
|||
///
|
||||
/// The buffer line is in an invalid state after this is called. See [`Self::reset_new`].
|
||||
pub(crate) fn reclaim_attrs(&mut self) -> AttrsList {
|
||||
mem::replace(&mut self.attrs_list, AttrsList::new(Attrs::new()))
|
||||
mem::replace(&mut self.attrs_list, AttrsList::new(&Attrs::new()))
|
||||
}
|
||||
|
||||
/// Reclaim text memory that isn't needed any longer.
|
||||
|
|
|
|||
|
|
@ -380,7 +380,7 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> {
|
|||
let line = BufferLine::new(
|
||||
String::new(),
|
||||
ending,
|
||||
AttrsList::new(attrs_list.as_ref().map_or_else(
|
||||
AttrsList::new(&attrs_list.as_ref().map_or_else(
|
||||
|| {
|
||||
buffer
|
||||
.lines
|
||||
|
|
@ -404,7 +404,7 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> {
|
|||
|
||||
// Collect attributes
|
||||
let mut final_attrs = attrs_list.unwrap_or_else(|| {
|
||||
AttrsList::new(line.attrs_list().get_span(cursor.index.saturating_sub(1)))
|
||||
AttrsList::new(&line.attrs_list().get_span(cursor.index.saturating_sub(1)))
|
||||
});
|
||||
|
||||
// Append the inserted text, line by line
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ impl<'syntax_system, 'buffer> SyntaxEditor<'syntax_system, 'buffer> {
|
|||
foreground.a,
|
||||
));
|
||||
}
|
||||
line.set_attrs_list(AttrsList::new(attrs));
|
||||
line.set_attrs_list(AttrsList::new(&attrs));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -125,7 +125,7 @@ impl<'syntax_system, 'buffer> SyntaxEditor<'syntax_system, 'buffer> {
|
|||
|
||||
let text = fs::read_to_string(path)?;
|
||||
self.editor.with_buffer_mut(|buffer| {
|
||||
buffer.set_text(font_system, &text, attrs, Shaping::Advanced);
|
||||
buffer.set_text(font_system, &text, &attrs, Shaping::Advanced);
|
||||
});
|
||||
|
||||
//TODO: re-use text
|
||||
|
|
@ -332,9 +332,11 @@ impl<'buffer> Edit<'buffer> for SyntaxEditor<'_, 'buffer> {
|
|||
);
|
||||
|
||||
let attrs = line.attrs_list().defaults();
|
||||
let mut attrs_list = AttrsList::new(attrs);
|
||||
let mut attrs_list = AttrsList::new(&attrs);
|
||||
let original_attrs = attrs.clone(); // Store a clone for comparison
|
||||
for (style, _, range) in ranges {
|
||||
let span_attrs = attrs
|
||||
.clone() // Clone attrs for modification
|
||||
.color(Color::rgba(
|
||||
style.foreground.r,
|
||||
style.foreground.g,
|
||||
|
|
@ -352,8 +354,8 @@ impl<'buffer> Edit<'buffer> for SyntaxEditor<'_, 'buffer> {
|
|||
} else {
|
||||
Weight::NORMAL
|
||||
}); //TODO: underline
|
||||
if span_attrs != attrs {
|
||||
attrs_list.add_span(range, span_attrs);
|
||||
if span_attrs != original_attrs {
|
||||
attrs_list.add_span(range, &span_attrs);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use unicode_script::Script;
|
|||
|
||||
use super::Fallback;
|
||||
|
||||
/// A platform-specific font fallback list, for MacOS.
|
||||
/// A platform-specific font fallback list, for `MacOS`.
|
||||
#[derive(Debug)]
|
||||
pub struct PlatformFallback;
|
||||
|
||||
|
|
|
|||
|
|
@ -306,7 +306,7 @@ impl FontSystem {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn get_font_matches(&mut self, attrs: Attrs<'_>) -> Arc<Vec<FontMatchKey>> {
|
||||
pub fn get_font_matches(&mut self, attrs: &Attrs<'_>) -> Arc<Vec<FontMatchKey>> {
|
||||
// Clear the cache first if it reached the size limit
|
||||
if self.font_matches_cache.len() >= Self::FONT_MATCHES_CACHE_SIZE_LIMIT {
|
||||
log::trace!("clear font mache cache");
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
//! let attrs = Attrs::new();
|
||||
//!
|
||||
//! // Add some text!
|
||||
//! buffer.set_text("Hello, Rust! 🦀\n", attrs, Shaping::Advanced);
|
||||
//! buffer.set_text("Hello, Rust! 🦀\n", &attrs, Shaping::Advanced);
|
||||
//!
|
||||
//! // Perform shaping as desired
|
||||
//! buffer.shape_until_scroll(true);
|
||||
|
|
|
|||
20
src/shape.rs
20
src/shape.rs
|
|
@ -140,12 +140,24 @@ fn shape_fallback(
|
|||
let rtl = matches!(buffer.direction(), rustybuzz::Direction::RightToLeft);
|
||||
assert_eq!(rtl, span_rtl);
|
||||
|
||||
let attrs = attrs_list.get_span(start_run);
|
||||
let mut rb_font_features = Vec::new();
|
||||
|
||||
// Convert attrs::Feature to rustybuzz::Feature
|
||||
for feature in attrs.font_features.features {
|
||||
rb_font_features.push(rustybuzz::Feature::new(
|
||||
rustybuzz::ttf_parser::Tag::from_bytes(feature.tag.as_bytes()),
|
||||
feature.value,
|
||||
0..usize::MAX,
|
||||
));
|
||||
}
|
||||
|
||||
let shape_plan = rustybuzz::ShapePlan::new(
|
||||
font.rustybuzz(),
|
||||
buffer.direction(),
|
||||
Some(buffer.script()),
|
||||
buffer.language().as_ref(),
|
||||
&[],
|
||||
&rb_font_features,
|
||||
);
|
||||
let glyph_buffer = rustybuzz::shape_with_plan(font.rustybuzz(), &shape_plan, buffer);
|
||||
let glyph_infos = glyph_buffer.glyph_infos();
|
||||
|
|
@ -249,7 +261,7 @@ fn shape_run(
|
|||
|
||||
let attrs = attrs_list.get_span(start_run);
|
||||
|
||||
let fonts = font_system.get_font_matches(attrs);
|
||||
let fonts = font_system.get_font_matches(&attrs);
|
||||
|
||||
let default_families = [&attrs.family];
|
||||
let mut font_iter = FontFallbackIter::new(
|
||||
|
|
@ -377,7 +389,7 @@ fn shape_run_cached(
|
|||
let run_range = start_run..end_run;
|
||||
let mut key = ShapeRunKey {
|
||||
text: line[run_range.clone()].to_string(),
|
||||
default_attrs: AttrsOwned::new(attrs_list.defaults()),
|
||||
default_attrs: AttrsOwned::new(&attrs_list.defaults()),
|
||||
attrs_spans: Vec::new(),
|
||||
};
|
||||
for (attrs_range, attrs) in attrs_list.spans.overlapping(&run_range) {
|
||||
|
|
@ -432,7 +444,7 @@ fn shape_skip(
|
|||
end_run: usize,
|
||||
) {
|
||||
let attrs = attrs_list.get_span(start_run);
|
||||
let fonts = font_system.get_font_matches(attrs);
|
||||
let fonts = font_system.get_font_matches(&attrs);
|
||||
|
||||
let default_families = [&attrs.family];
|
||||
let mut font_iter = FontFallbackIter::new(font_system, &fonts, &default_families, &[], "");
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ impl Default for DrawTestCfg {
|
|||
let font = Attrs::new().family(Family::Serif);
|
||||
Self {
|
||||
name: "default".into(),
|
||||
font: AttrsOwned::new(font),
|
||||
font: AttrsOwned::new(&font),
|
||||
text: "".into(),
|
||||
font_size: 16.0,
|
||||
line_height: 20.0,
|
||||
|
|
@ -60,7 +60,7 @@ impl DrawTestCfg {
|
|||
}
|
||||
|
||||
pub fn font_attrs(mut self, attrs: Attrs) -> Self {
|
||||
self.font = AttrsOwned::new(attrs);
|
||||
self.font = AttrsOwned::new(&attrs);
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -92,7 +92,7 @@ impl DrawTestCfg {
|
|||
Some((self.canvas_width - margins * 2) as f32),
|
||||
Some((self.canvas_height - margins * 2) as f32),
|
||||
);
|
||||
buffer.set_text(&self.text, self.font.as_attrs(), Shaping::Advanced);
|
||||
buffer.set_text(&self.text, &self.font.as_attrs(), Shaping::Advanced);
|
||||
buffer.shape_until_scroll(true);
|
||||
|
||||
// Black
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use cosmic_text::{
|
|||
fn stable_wrap() {
|
||||
let font_size = 18.0;
|
||||
let attrs = AttrsList::new(
|
||||
Attrs::new()
|
||||
&Attrs::new()
|
||||
.family(Family::Name("FiraMono"))
|
||||
.weight(Weight::MEDIUM),
|
||||
);
|
||||
|
|
@ -105,7 +105,7 @@ fn wrap_extra_line() {
|
|||
|
||||
// Add some text!
|
||||
buffer.set_wrap(Wrap::Word);
|
||||
buffer.set_text("Lorem ipsum dolor sit amet, qui minim labore adipisicing\n\nweeewoooo minim sint cillum sint consectetur cupidatat.", Attrs::new().family(cosmic_text::Family::Name("Inter")), Shaping::Advanced);
|
||||
buffer.set_text("Lorem ipsum dolor sit amet, qui minim labore adipisicing\n\nweeewoooo minim sint cillum sint consectetur cupidatat.", &Attrs::new().family(cosmic_text::Family::Name("Inter")), Shaping::Advanced);
|
||||
|
||||
// Set a size for the text buffer, in pixels
|
||||
buffer.set_size(Some(50.0), Some(1000.0));
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ fn wrap_word_fallback() {
|
|||
let mut buffer = buffer.borrow_with(&mut font_system);
|
||||
|
||||
buffer.set_wrap(Wrap::WordOrGlyph);
|
||||
buffer.set_text("Lorem ipsum dolor sit amet, qui minim labore adipisicing minim sint cillum sint consectetur cupidatat.", Attrs::new().family(cosmic_text::Family::Name("Inter")), Shaping::Advanced);
|
||||
buffer.set_text("Lorem ipsum dolor sit amet, qui minim labore adipisicing minim sint cillum sint consectetur cupidatat.", &Attrs::new().family(cosmic_text::Family::Name("Inter")), Shaping::Advanced);
|
||||
buffer.set_size(Some(50.0), Some(1000.0));
|
||||
|
||||
buffer.shape_until_scroll(false);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue