use super::TextDisplay;
use crate::conv::to_usize;
use crate::fonts::{fonts, FontId, ScaledFaceRef};
use crate::{Glyph, Vec2};
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Effect<X> {
pub start: u32,
pub flags: EffectFlags,
pub aux: X,
}
bitflags::bitflags! {
#[derive(Default)]
pub struct EffectFlags: u32 {
const UNDERLINE = 1 << 0;
const STRIKETHROUGH = 1 << 1;
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq)]
pub struct MarkerPos {
pub pos: Vec2,
pub ascent: f32,
pub descent: f32,
level: u8,
}
impl MarkerPos {
#[inline]
pub fn embedding_level(&self) -> u8 {
self.level
}
#[inline]
pub fn is_ltr(&self) -> bool {
self.level % 2 == 0
}
#[inline]
pub fn is_rtl(&self) -> bool {
self.level % 2 == 1
}
}
pub struct MarkerPosIter {
v: [MarkerPos; 2],
a: usize,
b: usize,
}
impl MarkerPosIter {
pub fn as_slice(&self) -> &[MarkerPos] {
&self.v[self.a..self.b]
}
}
impl Iterator for MarkerPosIter {
type Item = MarkerPos;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.a < self.b {
let i = self.a;
self.a = i + 1;
Some(self.v[i])
} else {
None
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.b - self.a;
(len, Some(len))
}
}
impl DoubleEndedIterator for MarkerPosIter {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
if self.a < self.b {
let i = self.b - 1;
self.b = i;
Some(self.v[i])
} else {
None
}
}
}
impl ExactSizeIterator for MarkerPosIter {}
impl TextDisplay {
pub fn text_glyph_pos(&self, index: usize) -> MarkerPosIter {
assert!(self.action.is_ready(), "kas-text::TextDisplay: not ready");
let mut v: [MarkerPos; 2] = Default::default();
let (a, mut b) = (0, 0);
let mut push_result = |pos, ascent, descent, level| {
v[b] = MarkerPos {
pos,
ascent,
descent,
level,
};
b += 1;
};
'a: for run_part in &self.wrapped_runs {
if index > to_usize(run_part.text_end) {
continue;
}
let glyph_run = &self.runs[to_usize(run_part.glyph_run)];
let sf = fonts().get(glyph_run.font_id).scale_by_dpu(glyph_run.dpu);
if index == to_usize(run_part.text_end) {
let i = if glyph_run.level.is_ltr() {
run_part.glyph_range.end()
} else {
run_part.glyph_range.start()
};
let pos = if i < glyph_run.glyphs.len() {
glyph_run.glyphs[i].position
} else {
Vec2(glyph_run.caret, 0.0)
};
let pos = run_part.offset + pos;
push_result(pos, sf.ascent(), sf.descent(), glyph_run.level.number());
continue;
}
let pos = 'b: loop {
if glyph_run.level.is_ltr() {
for glyph in glyph_run.glyphs[run_part.glyph_range.to_std()].iter().rev() {
if to_usize(glyph.index) <= index {
break 'b glyph.position;
}
}
} else {
for glyph in glyph_run.glyphs[run_part.glyph_range.to_std()].iter() {
if to_usize(glyph.index) <= index {
let mut pos = glyph.position;
pos.0 += sf.h_advance(glyph.id);
break 'b pos;
}
}
}
break 'a;
};
let pos = run_part.offset + pos;
push_result(pos, sf.ascent(), sf.descent(), glyph_run.level.number());
break;
}
MarkerPosIter { v, a, b }
}
#[inline]
pub fn num_glyphs(&self) -> usize {
to_usize(self.num_glyphs)
}
pub fn glyphs<F: FnMut(FontId, f32, f32, Glyph)>(&self, mut f: F) {
assert!(self.action.is_ready(), "kas-text::TextDisplay: not ready");
for run_part in self.wrapped_runs.iter().cloned() {
let run = &self.runs[to_usize(run_part.glyph_run)];
let font_id = run.font_id;
let dpu = run.dpu.0;
let height = run.height;
for mut glyph in run.glyphs[run_part.glyph_range.to_std()].iter().cloned() {
glyph.position = glyph.position + run_part.offset;
f(font_id, dpu, height, glyph);
}
}
}
pub fn glyphs_with_effects<X, F, G>(&self, effects: &[Effect<X>], mut f: F, mut g: G)
where
X: Copy + Default,
F: FnMut(FontId, f32, f32, Glyph, usize, X),
G: FnMut(f32, f32, f32, f32, usize, X),
{
assert!(self.action.is_ready(), "kas-text::TextDisplay: not ready");
let fonts = fonts();
let mut effect_cur = usize::MAX;
let mut effect_next = 0;
let mut next_start = effects
.get(effect_next)
.map(|e| e.start)
.unwrap_or(u32::MAX);
for run_part in self.wrapped_runs.iter().cloned() {
if run_part.glyph_range.len() == 0 {
continue;
}
let run = &self.runs[to_usize(run_part.glyph_run)];
let font_id = run.font_id;
let dpu = run.dpu.0;
let height = run.height;
let mut underline = None;
let mut strikethrough = None;
let ltr = run.level.is_ltr();
if !ltr {
let last_index = run.glyphs[run_part.glyph_range.start()].index;
while effects
.get(effect_next)
.map(|e| e.start <= last_index)
.unwrap_or(false)
{
effect_cur = effect_next;
effect_next += 1;
}
next_start = effects
.get(effect_next)
.map(|e| e.start)
.unwrap_or(u32::MAX);
}
let mut fmt = effects
.get(effect_cur)
.cloned()
.unwrap_or(Default::default());
if !fmt.flags.is_empty() {
let sf = fonts.get(run.font_id).scale_by_dpu(run.dpu);
let glyph = &run.glyphs[run_part.glyph_range.start()];
let position = glyph.position + run_part.offset;
if fmt.flags.contains(EffectFlags::UNDERLINE) {
if let Some(metrics) = sf.underline_metrics() {
let y_top = position.1 - metrics.position;
let h = metrics.thickness;
let x1 = position.0;
underline = Some((x1, y_top, h, fmt.aux));
}
}
if fmt.flags.contains(EffectFlags::STRIKETHROUGH) {
if let Some(metrics) = sf.strikethrough_metrics() {
let y_top = position.1 - metrics.position;
let h = metrics.thickness;
let x1 = position.0;
strikethrough = Some((x1, y_top, h, fmt.aux));
}
}
}
for mut glyph in run.glyphs[run_part.glyph_range.to_std()].iter().cloned() {
glyph.position = glyph.position + run_part.offset;
if (ltr && next_start <= glyph.index) || (!ltr && fmt.start > glyph.index) {
if ltr {
loop {
effect_cur = effect_next;
effect_next += 1;
if effects
.get(effect_next)
.map(|e| e.start > glyph.index)
.unwrap_or(true)
{
break;
}
}
next_start = effects
.get(effect_next)
.map(|e| e.start)
.unwrap_or(u32::MAX);
} else {
loop {
effect_cur = effect_cur.wrapping_sub(1);
if effects.get(effect_cur).map(|e| e.start).unwrap_or(0) <= glyph.index
{
break;
}
}
}
fmt = effects
.get(effect_cur)
.cloned()
.unwrap_or(Default::default());
if underline.is_some() != fmt.flags.contains(EffectFlags::UNDERLINE) {
let sf = fonts.get(run.font_id).scale_by_dpu(run.dpu);
if let Some((x1, y_top, h, aux)) = underline {
let x2 = glyph.position.0;
g(x1, x2, y_top, h, effect_cur, aux);
underline = None;
} else if let Some(metrics) = sf.underline_metrics() {
let y_top = glyph.position.1 - metrics.position;
let h = metrics.thickness;
let x1 = glyph.position.0;
underline = Some((x1, y_top, h, fmt.aux));
}
}
if strikethrough.is_some() != fmt.flags.contains(EffectFlags::STRIKETHROUGH) {
let sf = fonts.get(run.font_id).scale_by_dpu(run.dpu);
if let Some((x1, y_top, h, aux)) = strikethrough {
let x2 = glyph.position.0;
g(x1, x2, y_top, h, effect_cur, aux);
strikethrough = None;
} else if let Some(metrics) = sf.strikethrough_metrics() {
let y_top = glyph.position.1 - metrics.position;
let h = metrics.thickness;
let x1 = glyph.position.0;
strikethrough = Some((x1, y_top, h, fmt.aux));
}
}
}
f(font_id, dpu, height, glyph, effect_cur, fmt.aux);
}
effect_cur = effect_next.wrapping_sub(1);
if let Some((x1, y_top, h, aux)) = underline {
let x2 = if run_part.glyph_range.end() < run.glyphs.len() {
run.glyphs[run_part.glyph_range.end()].position.0
} else {
run.caret
} + run_part.offset.0;
g(x1, x2, y_top, h, effect_cur, aux);
}
if let Some((x1, y_top, h, aux)) = strikethrough {
let x2 = if run_part.glyph_range.end() < run.glyphs.len() {
run.glyphs[run_part.glyph_range.end()].position.0
} else {
run.caret
} + run_part.offset.0;
g(x1, x2, y_top, h, effect_cur, aux);
}
}
}
pub fn highlight_lines(&self, range: std::ops::Range<usize>) -> Vec<(Vec2, Vec2)> {
assert!(self.action.is_ready(), "kas-text::TextDisplay: not ready");
if range.len() == 0 {
return vec![];
}
let mut lines = self.lines.iter();
let mut rects = Vec::with_capacity(self.lines.len());
let rbound = self.width;
let mut cur_line = 'l1: loop {
while let Some(line) = lines.next() {
if line.text_range.includes(range.start) {
break 'l1 line;
}
}
return vec![];
};
if range.start > cur_line.text_range.start() || range.end <= cur_line.text_range.end() {
self.highlight_run_range(range.clone(), cur_line.run_range.to_std(), &mut rects);
if !rects.is_empty() && range.end > cur_line.text_range.end() {
let mut nearest = 0;
let first_run = cur_line.run_range.start();
let glyph_run = to_usize(self.wrapped_runs[first_run].glyph_run);
if self.runs[glyph_run].level.is_ltr() {
let mut dist = rbound - (rects[0].1).0;
for i in 1..rects.len() {
let d = rbound - (rects[i].1).0;
if d < dist {
nearest = i;
dist = d;
}
}
(rects[nearest].1).0 = rbound;
} else {
let mut dist = (rects[0].0).0;
for i in 1..rects.len() {
let d = (rects[i].0).0;
if d < dist {
nearest = i;
dist = d;
}
}
(rects[nearest].0).0 = 0.0;
}
}
} else {
let a = Vec2(0.0, cur_line.top);
let b = Vec2(rbound, cur_line.bottom);
rects.push((a, b));
}
if range.end > cur_line.text_range.end() {
while let Some(line) = lines.next() {
if range.end <= line.text_range.end() {
cur_line = line;
break;
}
let a = Vec2(0.0, line.top);
let b = Vec2(rbound, line.bottom);
rects.push((a, b));
}
if cur_line.text_range.start() < range.end {
self.highlight_run_range(range, cur_line.run_range.to_std(), &mut rects);
}
}
rects
}
#[inline]
pub fn highlight_runs(&self, range: std::ops::Range<usize>) -> Vec<(Vec2, Vec2)> {
assert!(self.action.is_ready(), "kas-text::TextDisplay: not ready");
if range.len() == 0 {
return vec![];
}
let mut rects = Vec::with_capacity(self.wrapped_runs.len());
self.highlight_run_range(range, 0..usize::MAX, &mut rects);
rects
}
fn highlight_run_range(
&self,
range: std::ops::Range<usize>,
run_range: std::ops::Range<usize>,
rects: &mut Vec<(Vec2, Vec2)>,
) {
let mut push_rect = |mut a: Vec2, mut b: Vec2, sf: ScaledFaceRef| {
a.1 -= sf.ascent();
b.1 -= sf.descent();
rects.push((a, b));
};
let mut a;
let mut i = run_range.start;
'b: loop {
if i >= run_range.end {
return;
}
let run_part = &self.wrapped_runs[i];
if range.start >= to_usize(run_part.text_end) {
i += 1;
continue;
}
let glyph_run = &self.runs[to_usize(run_part.glyph_run)];
let sf = fonts().get(glyph_run.font_id).scale_by_dpu(glyph_run.dpu);
if glyph_run.level.is_ltr() {
for glyph in glyph_run.glyphs[run_part.glyph_range.to_std()].iter().rev() {
if to_usize(glyph.index) <= range.start {
a = glyph.position;
break 'b;
}
}
a = Vec2::ZERO;
} else {
for glyph in glyph_run.glyphs[run_part.glyph_range.to_std()].iter() {
if to_usize(glyph.index) <= range.start {
a = glyph.position;
a.0 += sf.h_advance(glyph.id);
break 'b;
}
}
a = Vec2(glyph_run.caret, 0.0);
}
break 'b;
}
let mut first = true;
'a: while i < run_range.end {
let run_part = &self.wrapped_runs[i];
let offset = run_part.offset;
let glyph_run = &self.runs[to_usize(run_part.glyph_run)];
let sf = fonts().get(glyph_run.font_id).scale_by_dpu(glyph_run.dpu);
if !first {
a = if glyph_run.level.is_ltr() {
if run_part.glyph_range.start() < glyph_run.glyphs.len() {
glyph_run.glyphs[run_part.glyph_range.start()].position
} else {
Vec2::ZERO
}
} else {
if run_part.glyph_range.end() < glyph_run.glyphs.len() {
glyph_run.glyphs[run_part.glyph_range.end()].position
} else {
Vec2(glyph_run.caret, 0.0)
}
};
}
first = false;
if range.end >= to_usize(run_part.text_end) {
let b;
if glyph_run.level.is_ltr() {
if run_part.glyph_range.end() < glyph_run.glyphs.len() {
b = glyph_run.glyphs[run_part.glyph_range.end()].position;
} else {
b = Vec2(glyph_run.caret, 0.0);
}
} else {
let p = if run_part.glyph_range.start() < glyph_run.glyphs.len() {
glyph_run.glyphs[run_part.glyph_range.start()].position
} else {
Vec2(glyph_run.caret, 0.0)
};
b = Vec2(a.0, p.1);
a.0 = p.0;
};
push_rect(a + offset, b + offset, sf);
i += 1;
continue;
}
let b;
'c: loop {
if glyph_run.level.is_ltr() {
for glyph in glyph_run.glyphs[run_part.glyph_range.to_std()].iter().rev() {
if to_usize(glyph.index) <= range.end {
b = glyph.position;
break 'c;
}
}
} else {
for glyph in glyph_run.glyphs[run_part.glyph_range.to_std()].iter() {
if to_usize(glyph.index) <= range.end {
let mut p = glyph.position;
p.0 += sf.h_advance(glyph.id);
b = Vec2(a.0, p.1);
a.0 = p.0;
break 'c;
}
}
}
break 'a;
}
push_rect(a + offset, b + offset, sf);
break;
}
}
}