Skip to content

Commit 7bf8338

Browse files
committed
Merge stable into master
2 parents 099f404 + d18356f commit 7bf8338

File tree

8 files changed

+130
-248
lines changed

8 files changed

+130
-248
lines changed

src/display/glyph_pos.rs

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,11 @@ pub struct Effect {
2424
/// (Note that we use `u32` not `usize` since it can be assumed text length
2525
/// will never exceed `u32::MAX`.)
2626
pub start: u32,
27-
/// User-specified value, e.g. index into a colour palette
28-
pub e: u16,
27+
/// User-specified value
28+
///
29+
/// Usage is not specified by `kas-text`, but typically this field will be
30+
/// used as an index into a colour palette or not used at all.
31+
pub color: u16,
2932
/// Effect flags
3033
pub flags: EffectFlags,
3134
}
@@ -170,8 +173,8 @@ impl<'a> GlyphRun<'a> {
170173

171174
/// Yield glyphs and effects for this run
172175
///
173-
/// The callback `f` receives `glyph, e` where `e` is the [`Effect::e`]
174-
/// value (defaults to 0).
176+
/// The callback `f` receives `glyph, color` where `color` is the
177+
/// [`Effect::color`] value (defaults to 0).
175178
///
176179
/// The callback `g` receives positioning for each underline/strike-through
177180
/// segment: `x1, x2, y_top, h` where `h` is the thickness (height). Note
@@ -235,15 +238,15 @@ impl<'a> GlyphRun<'a> {
235238
let y_top = position.1 - metrics.position;
236239
let h = metrics.thickness;
237240
let x1 = position.0;
238-
underline = Some((x1, y_top, h, fmt.e));
241+
underline = Some((x1, y_top, h, fmt.color));
239242
}
240243
if fmt.flags.contains(EffectFlags::STRIKETHROUGH)
241244
&& let Some(metrics) = sf.strikethrough_metrics()
242245
{
243246
let y_top = position.1 - metrics.position;
244247
let h = metrics.thickness;
245248
let x1 = position.0;
246-
strikethrough = Some((x1, y_top, h, fmt.e));
249+
strikethrough = Some((x1, y_top, h, fmt.color));
247250
}
248251
}
249252

@@ -297,7 +300,7 @@ impl<'a> GlyphRun<'a> {
297300
let y_top = glyph.position.1 - metrics.position;
298301
let h = metrics.thickness;
299302
let x1 = glyph.position.0;
300-
underline = Some((x1, y_top, h, fmt.e));
303+
underline = Some((x1, y_top, h, fmt.color));
301304
}
302305
}
303306
if strikethrough.is_some() != fmt.flags.contains(EffectFlags::STRIKETHROUGH) {
@@ -309,12 +312,12 @@ impl<'a> GlyphRun<'a> {
309312
let y_top = glyph.position.1 - metrics.position;
310313
let h = metrics.thickness;
311314
let x1 = glyph.position.0;
312-
strikethrough = Some((x1, y_top, h, fmt.e));
315+
strikethrough = Some((x1, y_top, h, fmt.color));
313316
}
314317
}
315318
}
316319

317-
f(glyph, fmt.e);
320+
f(glyph, fmt.color);
318321
}
319322

320323
// Effects end at the following glyph's start (or end of this run part)
@@ -446,8 +449,11 @@ impl TextDisplay {
446449
/// free).
447450
///
448451
/// An [`Effect`] sequence supports underline, strikethrough and custom
449-
/// indexing (e.g. for a color palette). Pass `&[]` if effects are not
450-
/// required. (The default effect is always [`Effect::default()`].)
452+
/// indexing (e.g. for a color palette). This sequence may be the result of
453+
/// [`FormattableText::effect_tokens`], `&[]`, or any other sequence such
454+
/// that [`Effect::start`] values are strictly increasing and compatible
455+
/// with text `char` indices (see also [`FormattableText::effect_tokens`]).
456+
/// (It is not required to re-prepare text when changing the sequence.)
451457
///
452458
/// Runs are yielded in undefined order. The total number of
453459
/// glyphs yielded will equal [`TextDisplay::num_glyphs`].
@@ -459,6 +465,21 @@ impl TextDisplay {
459465
offset: Vec2,
460466
effects: &'a [Effect],
461467
) -> impl Iterator<Item = GlyphRun<'a>> + 'a {
468+
#[cfg(debug_assertions)]
469+
{
470+
let mut start = None;
471+
for effect in effects {
472+
if let Some(i) = start
473+
&& effect.start <= i
474+
{
475+
panic!(
476+
"TextDisplay::runs: Effect::start indices are not strictly increasing in {effects:?}"
477+
);
478+
}
479+
start = Some(effect.start);
480+
}
481+
}
482+
462483
self.wrapped_runs
463484
.iter()
464485
.filter(|part| !part.glyph_range.is_empty())

src/display/text_runs.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,13 @@ impl TextDisplay {
3838
///
3939
/// This updates the result of [`TextDisplay::prepare_runs`] due to change
4040
/// in font size.
41-
pub fn resize_runs<F: FormattableText + ?Sized>(&mut self, text: &F, mut dpem: f32) {
42-
let mut font_tokens = text.font_tokens(dpem);
41+
pub fn resize_runs<F: FormattableText + ?Sized>(
42+
&mut self,
43+
text: &F,
44+
font: FontSelector,
45+
mut dpem: f32,
46+
) {
47+
let mut font_tokens = text.font_tokens(dpem, font);
4348
let mut next_fmt = font_tokens.next();
4449

4550
let text = text.as_str();
@@ -171,7 +176,7 @@ impl TextDisplay {
171176

172177
self.runs.clear();
173178

174-
let mut font_tokens = text.font_tokens(dpem);
179+
let mut font_tokens = text.font_tokens(dpem, font);
175180
let mut next_fmt = font_tokens.next();
176181
if let Some(fmt) = next_fmt.as_ref()
177182
&& fmt.start == 0

src/format.rs

Lines changed: 29 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55

66
//! Parsers for formatted text
77
8+
use crate::Effect;
89
use crate::fonts::FontSelector;
9-
use crate::{Effect, OwningVecIter};
1010
#[allow(unused)]
1111
use crate::{Text, TextDisplay}; // for doc-links
1212

@@ -18,14 +18,7 @@ mod markdown;
1818
pub use markdown::{Error as MarkdownError, Markdown};
1919

2020
/// Text, optionally with formatting data
21-
///
22-
/// Any `F: FormattableText` automatically support [`FormattableTextDyn`].
23-
/// Implement either this or [`FormattableTextDyn`], not both.
24-
pub trait FormattableText: std::cmp::PartialEq + std::fmt::Debug {
25-
type FontTokenIter<'a>: Iterator<Item = FontToken>
26-
where
27-
Self: 'a;
28-
21+
pub trait FormattableText: std::cmp::PartialEq {
2922
/// Length of text
3023
///
3124
/// Default implementation uses [`FormattableText::as_str`].
@@ -37,148 +30,53 @@ pub trait FormattableText: std::cmp::PartialEq + std::fmt::Debug {
3730
/// Access whole text as contiguous `str`
3831
fn as_str(&self) -> &str;
3932

40-
/// Construct an iterator over formatting items
33+
/// Return an iterator of font tokens
4134
///
42-
/// It is expected that [`FontToken::start`] of yielded items is strictly
43-
/// increasing; if not, formatting may not be applied correctly.
35+
/// These tokens are used to select the font and font size.
36+
/// Each text object has a configured
37+
/// [font size][crate::Text::set_font_size] and [`FontSelector`]; these
38+
/// values are passed as a reference (`dpem` and `font`).
4439
///
45-
/// The default [font size][crate::Text::set_font_size] (`dpem`) is passed
46-
/// as a reference.
40+
/// The iterator is expected to yield a stream of tokens such that
41+
/// [`FontToken::start`] values are strictly increasing, less than
42+
/// [`Self::str_len`] and at `char` boundaries (i.e. an index value returned
43+
/// by [`str::char_indices`]. In case the returned iterator is empty or the
44+
/// first [`FontToken::start`] value is greater than zero the reference
45+
/// `dpem` and `font` values are used.
4746
///
48-
/// For plain text this iterator will be empty.
49-
fn font_tokens<'a>(&'a self, dpem: f32) -> Self::FontTokenIter<'a>;
47+
/// Any changes to the result of this method require full re-preparation of
48+
/// text since this affects run breaking and font resolution.
49+
fn font_tokens(&self, dpem: f32, font: FontSelector) -> impl Iterator<Item = FontToken>;
5050

51-
/// Get the sequence of effect tokens
51+
/// Return the sequence of effect tokens
52+
///
53+
/// These tokens are used to select the font color and
54+
/// [effects](crate::EffectFlags).
5255
///
53-
/// This method has some limitations: (1) it may only return a reference to
54-
/// an existing sequence, (2) effect tokens cannot be generated dependent
55-
/// on input state, and (3) it does not incorporate color information. For
56-
/// most uses it should still be sufficient, but for other cases it may be
57-
/// preferable not to use this method (use a dummy implementation returning
58-
/// `&[]` and use inherent methods on the text object via [`Text::text`]).
56+
/// The values of [`Effect::start`] are expected to be strictly increasing
57+
/// in order, less than [`Self::str_len`]. In case the slice is empty or the
58+
/// first [`Effect::start`] value is greater than zero, values from
59+
/// [`Effect::default()`] are used.
60+
///
61+
/// Changes to the result of this method do not require any re-preparation
62+
/// of text.
5963
fn effect_tokens(&self) -> &[Effect];
6064
}
6165

6266
impl<F: FormattableText + ?Sized> FormattableText for &F {
63-
type FontTokenIter<'a>
64-
= F::FontTokenIter<'a>
65-
where
66-
Self: 'a;
67-
6867
fn as_str(&self) -> &str {
6968
F::as_str(self)
7069
}
7170

72-
fn font_tokens<'a>(&'a self, dpem: f32) -> Self::FontTokenIter<'a> {
73-
F::font_tokens(self, dpem)
71+
fn font_tokens(&self, dpem: f32, font: FontSelector) -> impl Iterator<Item = FontToken> {
72+
F::font_tokens(self, dpem, font)
7473
}
7574

7675
fn effect_tokens(&self) -> &[Effect] {
7776
F::effect_tokens(self)
7877
}
7978
}
8079

81-
/// Text, optionally with formatting data
82-
///
83-
/// This is an object-safe version of the [`FormattableText`] trait (i.e.
84-
/// `dyn FormattableTextDyn` is a valid type).
85-
///
86-
/// This trait is auto-implemented for every implementation of [`FormattableText`].
87-
/// The type `&dyn FormattableTextDyn` implements [`FormattableText`].
88-
/// Implement either this or (preferably) [`FormattableText`], not both.
89-
pub trait FormattableTextDyn: std::fmt::Debug {
90-
/// Produce a boxed clone of self
91-
fn clone_boxed(&self) -> Box<dyn FormattableTextDyn>;
92-
93-
/// Length of text
94-
fn str_len(&self) -> usize;
95-
96-
/// Access whole text as contiguous `str`
97-
fn as_str(&self) -> &str;
98-
99-
/// Construct an iterator over formatting items
100-
///
101-
/// It is expected that [`FontToken::start`] of yielded items is strictly
102-
/// increasing; if not, formatting may not be applied correctly.
103-
///
104-
/// The default [font size][crate::Text::set_font_size] (`dpem`) is passed
105-
/// as a reference.
106-
///
107-
/// For plain text this iterator will be empty.
108-
fn font_tokens(&self, dpem: f32) -> OwningVecIter<FontToken>;
109-
110-
/// Get the sequence of effect tokens
111-
///
112-
/// This method has some limitations: (1) it may only return a reference to
113-
/// an existing sequence, (2) effect tokens cannot be generated dependent
114-
/// on input state, and (3) it does not incorporate color information. For
115-
/// most uses it should still be sufficient, but for other cases it may be
116-
/// preferable not to use this method (use a dummy implementation returning
117-
/// `&[]` and use inherent methods on the text object via [`Text::text`]).
118-
fn effect_tokens(&self) -> &[Effect];
119-
}
120-
121-
impl<F: FormattableText + Clone + 'static> FormattableTextDyn for F {
122-
fn clone_boxed(&self) -> Box<dyn FormattableTextDyn> {
123-
Box::new(self.clone())
124-
}
125-
126-
fn str_len(&self) -> usize {
127-
FormattableText::str_len(self)
128-
}
129-
fn as_str(&self) -> &str {
130-
FormattableText::as_str(self)
131-
}
132-
133-
fn font_tokens(&self, dpem: f32) -> OwningVecIter<FontToken> {
134-
let iter = FormattableText::font_tokens(self, dpem);
135-
OwningVecIter::new(iter.collect())
136-
}
137-
138-
fn effect_tokens(&self) -> &[Effect] {
139-
FormattableText::effect_tokens(self)
140-
}
141-
}
142-
143-
/// References to [`FormattableTextDyn`] always compare unequal
144-
impl<'t> PartialEq for &'t dyn FormattableTextDyn {
145-
fn eq(&self, _: &Self) -> bool {
146-
false
147-
}
148-
}
149-
150-
impl<'t> FormattableText for &'t dyn FormattableTextDyn {
151-
type FontTokenIter<'a>
152-
= OwningVecIter<FontToken>
153-
where
154-
Self: 'a;
155-
156-
#[inline]
157-
fn str_len(&self) -> usize {
158-
FormattableTextDyn::str_len(*self)
159-
}
160-
161-
#[inline]
162-
fn as_str(&self) -> &str {
163-
FormattableTextDyn::as_str(*self)
164-
}
165-
166-
#[inline]
167-
fn font_tokens(&self, dpem: f32) -> OwningVecIter<FontToken> {
168-
FormattableTextDyn::font_tokens(*self, dpem)
169-
}
170-
171-
fn effect_tokens(&self) -> &[Effect] {
172-
FormattableTextDyn::effect_tokens(*self)
173-
}
174-
}
175-
176-
impl Clone for Box<dyn FormattableTextDyn> {
177-
fn clone(&self) -> Self {
178-
(**self).clone_boxed()
179-
}
180-
}
181-
18280
/// Font formatting token
18381
#[derive(Clone, Debug, PartialEq)]
18482
pub struct FontToken {

0 commit comments

Comments
 (0)