use std::any::{Any, type_name};
use skia_safe::Color;
use crate::controls::label::SkiaLabel;
use crate::tree::{Cx, Mut, Raw};
use crate::types::IntoProp;
pub(crate) type SpanTapped = Box<dyn FnMut(Raw<'_>, &mut dyn Any, &mut Cx<'_>)>;
pub struct TextSpan {
pub text: String,
pub text_color: Option<Color>,
pub font_size: Option<f32>,
pub font_family: Option<String>,
pub font_weight: i32,
pub is_bold: bool,
pub is_italic: bool,
pub underline: bool,
pub underline_width: f32,
pub strikeout: bool,
pub strikeout_width: f32,
pub strikeout_color: Color,
pub background_color: Option<Color>,
pub tag: String,
pub force_capture_input: bool,
pub is_visible: bool,
pub(crate) tapped: Option<SpanTapped>,
}
impl Default for TextSpan {
fn default() -> Self {
Self {
text: String::new(),
text_color: None,
font_size: None,
font_family: None,
font_weight: 0,
is_bold: false,
is_italic: false,
underline: false,
underline_width: -1.0,
strikeout: false,
strikeout_width: 1.0,
strikeout_color: Color::RED,
background_color: None,
tag: String::new(),
force_capture_input: false,
is_visible: true,
tapped: None,
}
}
}
macro_rules! setters {
($($name:ident: $ty:ty),* $(,)?) => {
impl TextSpan {
$(#[doc = concat!("Sets `", stringify!($name), "`.")]
pub fn $name(mut self, v: impl IntoProp<$ty>) -> Self {
self.$name = v.into_prop();
self
})*
}
};
}
setters!(
text: String,
text_color: Option<Color>,
font_weight: i32,
is_bold: bool,
is_italic: bool,
underline: bool,
underline_width: f32,
strikeout: bool,
strikeout_width: f32,
strikeout_color: Color,
background_color: Option<Color>,
tag: String,
force_capture_input: bool,
is_visible: bool,
);
impl TextSpan {
pub fn new(text: impl Into<String>) -> Self {
Self { text: text.into(), ..Self::default() }
}
pub fn font_size(mut self, v: impl IntoProp<f32>) -> Self {
self.font_size = Some(v.into_prop());
self
}
pub fn font_family(mut self, v: impl Into<String>) -> Self {
self.font_family = Some(v.into());
self
}
pub fn on_tapped<S: Any>(mut self, mut f: impl FnMut(&mut Mut<'_, SkiaLabel>, &mut S, &mut Cx<'_>) + 'static) -> Self {
self.tapped = Some(Box::new(move |raw, state, cx| {
let state = state.downcast_mut::<S>().unwrap_or_else(|| panic!("handler expects app state `{}`", type_name::<S>()));
f(&mut raw.typed(), state, cx)
}));
self
}
pub fn has_tap_handler(&self) -> bool {
self.tapped.is_some() || self.force_capture_input
}
}
pub trait IntoSpans {
fn push_into(self, out: &mut Vec<TextSpan>);
}
impl IntoSpans for TextSpan {
fn push_into(self, out: &mut Vec<TextSpan>) {
out.push(self)
}
}
impl IntoSpans for Vec<TextSpan> {
fn push_into(self, out: &mut Vec<TextSpan>) {
out.extend(self)
}
}
macro_rules! tuple_spans {
() => {};
($head:ident $($tail:ident)*) => {
impl<$head: IntoSpans, $($tail: IntoSpans),*> IntoSpans for ($head, $($tail,)*) {
#[allow(non_snake_case)]
fn push_into(self, out: &mut Vec<TextSpan>) {
let ($head, $($tail,)*) = self;
$head.push_into(out);
$($tail.push_into(out);)*
}
}
tuple_spans!($($tail)*);
};
}
tuple_spans!(A B C D E F G H I J K L M N O P Q R S T);