use smallvec::SmallVec;
use crate::cast::Conv;
use crate::event::Key;
use crate::text::format::{FontToken, FormattableText};
use crate::text::{Effect, EffectFlags};
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct AccessString {
label: String,
effects: SmallVec<[Effect<()>; 2]>,
key: Option<Key>,
}
impl AccessString {
fn parse(mut s: &str) -> Self {
let mut buf = String::with_capacity(s.len());
let mut effects = SmallVec::<[Effect<()>; 2]>::default();
let mut key = None;
while let Some(mut i) = s.find('&') {
buf.push_str(&s[..i]);
i += "&".len();
s = &s[i..];
let mut chars = s.char_indices();
match chars.next() {
None => {
s = &s[0..0];
break;
}
Some((j, c)) => {
let pos = u32::conv(buf.len());
buf.push(c);
if effects.last().map(|e| e.start == pos).unwrap_or(false) {
effects.last_mut().unwrap().flags = EffectFlags::UNDERLINE;
} else {
effects.push(Effect {
start: pos,
flags: EffectFlags::UNDERLINE,
aux: (),
});
}
if key.is_none() {
let mut kbuf = [0u8; 4];
let s = c.to_ascii_lowercase().encode_utf8(&mut kbuf);
key = Some(Key::Character(s.into()));
}
let i = c.len_utf8();
s = &s[i..];
if let Some((k, _)) = chars.next() {
effects.push(Effect {
start: pos + u32::conv(k - j),
flags: EffectFlags::empty(),
aux: (),
});
}
}
}
}
buf.push_str(s);
AccessString {
label: buf,
effects,
key,
}
}
pub fn key(&self) -> Option<&Key> {
self.key.as_ref()
}
pub fn text(&self) -> &str {
&self.label
}
}
impl FormattableText for AccessString {
type FontTokenIter<'a> = std::iter::Empty<FontToken>;
#[inline]
fn as_str(&self) -> &str {
&self.label
}
#[inline]
fn font_tokens(&self, _: f32) -> Self::FontTokenIter<'_> {
std::iter::empty()
}
fn effect_tokens(&self) -> &[Effect<()>] {
&self.effects
}
}
impl From<String> for AccessString {
fn from(input: String) -> Self {
if input.as_bytes().contains(&b'&') {
Self::parse(&input)
} else {
AccessString {
label: input,
..Default::default()
}
}
}
}
impl From<&str> for AccessString {
fn from(input: &str) -> Self {
Self::parse(input)
}
}
impl<T: Into<AccessString> + Copy> From<&T> for AccessString {
fn from(input: &T) -> Self {
(*input).into()
}
}