use crate::Color;
use std::rc::Rc;
use std::sync::Arc;
#[derive(Clone, Debug)]
pub struct TextLayoutResult {
pub line_count: usize,
pub width_px: f32,
pub height_px: f32,
}
pub trait VisualTransformation: std::fmt::Debug + Send + Sync + 'static {
fn filter(&self, text: &str) -> TransformedText;
}
pub struct TransformedText {
pub text: String,
pub offset_map: Rc<dyn Fn(usize) -> usize>,
}
impl Clone for TransformedText {
fn clone(&self) -> Self {
Self {
text: self.text.clone(),
offset_map: self.offset_map.clone(),
}
}
}
impl std::fmt::Debug for TransformedText {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TransformedText")
.field("text", &self.text)
.finish()
}
}
#[derive(Clone, Copy, Debug)]
pub struct NoVisualTransformation;
impl VisualTransformation for NoVisualTransformation {
fn filter(&self, text: &str) -> TransformedText {
let len = text.len();
TransformedText {
text: text.to_string(),
offset_map: Rc::new(move |offset| offset.min(len)),
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct PasswordVisualTransformation {
pub mask_char: char,
}
impl Default for PasswordVisualTransformation {
fn default() -> Self {
Self { mask_char: '*' }
}
}
impl VisualTransformation for PasswordVisualTransformation {
fn filter(&self, text: &str) -> TransformedText {
let masked: String = text.chars().map(|_| self.mask_char).collect();
let len = text.len();
TransformedText {
text: masked,
offset_map: Rc::new(move |offset| offset.min(len)),
}
}
}
pub fn original_offset_to_display(original: &str, display: &str, original_byte: usize) -> usize {
let char_idx = original[..original_byte.min(original.len())]
.chars()
.count();
display
.char_indices()
.nth(char_idx)
.map(|(i, _)| i)
.unwrap_or(display.len())
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum KeyboardType {
#[default]
Text,
Ascii,
Number,
Phone,
Email,
Uri,
Decimal,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum ImeAction {
#[default]
Unspecified,
None,
Go,
Search,
Send,
Next,
Done,
Previous,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct SpanStyle {
pub color: Option<Color>,
pub font_size: Option<f32>,
}
impl SpanStyle {
pub const fn default() -> Self {
Self {
color: None,
font_size: None,
}
}
pub fn color(mut self, c: Color) -> Self {
self.color = Some(c);
self
}
pub fn font_size(mut self, px: f32) -> Self {
self.font_size = Some(px);
self
}
}
impl Default for SpanStyle {
fn default() -> Self {
Self::default()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct TextSpan {
pub start: usize,
pub end: usize,
pub style: SpanStyle,
}
#[derive(Debug, Clone)]
pub struct AnnotatedString {
pub text: String,
pub spans: Arc<[TextSpan]>,
}
impl AnnotatedString {
pub fn new(text: impl Into<String>, spans: Vec<TextSpan>) -> Self {
let text = text.into();
Self {
text,
spans: spans.into(),
}
}
pub fn as_str(&self) -> &str {
&self.text
}
}
impl From<String> for AnnotatedString {
fn from(text: String) -> Self {
Self {
text,
spans: Arc::from([]),
}
}
}
impl From<&str> for AnnotatedString {
fn from(text: &str) -> Self {
Self {
text: text.to_string(),
spans: Arc::from([]),
}
}
}
#[derive(Default)]
pub struct AnnotatedStringBuilder {
text: String,
spans: Vec<TextSpan>,
}
impl AnnotatedStringBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn push(&mut self, text: &str) -> &mut Self {
self.text.push_str(text);
self
}
pub fn push_with_style(&mut self, text: &str, style: SpanStyle) -> &mut Self {
let start = self.text.len();
self.text.push_str(text);
let end = self.text.len();
if start < end {
self.spans.push(TextSpan { start, end, style });
}
self
}
pub fn push_color(&mut self, text: &str, color: Color) -> &mut Self {
self.push_with_style(text, SpanStyle::default().color(color))
}
pub fn add_style(&mut self, start: usize, end: usize, style: SpanStyle) -> &mut Self {
if start < end && end <= self.text.len() {
self.spans.push(TextSpan { start, end, style });
}
self
}
pub fn build(&mut self) -> AnnotatedString {
let text = std::mem::take(&mut self.text);
self.spans.sort_by_key(|s| s.start);
let mut merged: Vec<TextSpan> = Vec::new();
for span in std::mem::take(&mut self.spans) {
if let Some(last) = merged.last_mut()
&& last.end == span.start
&& last.style == span.style
{
last.end = span.end;
continue;
}
merged.push(span);
}
AnnotatedString {
text,
spans: merged.into(),
}
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub enum TextAlign {
Left,
Right,
Center,
Justify,
Start,
End,
Unspecified,
}
impl Default for TextAlign {
fn default() -> Self {
TextAlign::Unspecified
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct FontWeight(pub u16);
impl FontWeight {
pub const THIN: FontWeight = FontWeight(100);
pub const EXTRA_LIGHT: FontWeight = FontWeight(200);
pub const LIGHT: FontWeight = FontWeight(300);
pub const NORMAL: FontWeight = FontWeight(400);
pub const MEDIUM: FontWeight = FontWeight(500);
pub const SEMI_BOLD: FontWeight = FontWeight(600);
pub const BOLD: FontWeight = FontWeight(700);
pub const EXTRA_BOLD: FontWeight = FontWeight(800);
pub const BLACK: FontWeight = FontWeight(900);
}
impl Default for FontWeight {
fn default() -> Self {
FontWeight::NORMAL
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub enum FontStyle {
Normal,
Italic,
}
impl Default for FontStyle {
fn default() -> Self {
FontStyle::Normal
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct TextDecoration {
pub underline: bool,
pub strikethrough: bool,
pub color: Option<Color>,
}
impl Default for TextDecoration {
fn default() -> Self {
Self {
underline: false,
strikethrough: false,
color: None,
}
}
}
pub fn build_annotated_string(b: impl FnOnce(&mut AnnotatedStringBuilder)) -> AnnotatedString {
let mut builder = AnnotatedStringBuilder::new();
b(&mut builder);
builder.build()
}