use crate::{
ContainerAtom, FontSelection, Image, ImageSource, SizedAtomKind, Ui, WidgetAtom, WidgetText,
};
use alloc::sync::Arc;
use core::fmt::Debug;
use emath::{Rect, Vec2};
use epaint::Color32;
use epaint::text::TextWrapMode;
pub struct IntoSizedArgs {
pub available_size: Vec2,
pub wrap_mode: TextWrapMode,
pub fallback_font: FontSelection,
}
pub struct IntoSizedResult<'a> {
pub intrinsic_size: Vec2,
pub sized: SizedAtomKind<'a>,
}
pub type AtomClosure<'a> = Box<dyn FnOnce(&Ui, IntoSizedArgs) -> IntoSizedResult<'static> + 'a>;
#[derive(Clone, Copy, Debug)]
pub struct AtomPaintArgs {
pub rect: Rect,
pub fallback_text_color: Color32,
}
pub type AtomPaint<'a> = Arc<dyn Fn(&Ui, AtomPaintArgs) + 'a>;
#[derive(Default)]
pub enum AtomKind<'a> {
#[default]
Empty,
Text(WidgetText),
Image(Image<'a>),
Closure(AtomClosure<'a>),
Paint(AtomPaint<'a>),
Widget(Box<WidgetAtom<'a>>),
Container(Box<ContainerAtom<'a>>),
}
impl Clone for AtomKind<'_> {
fn clone(&self) -> Self {
match self {
AtomKind::Empty => AtomKind::Empty,
AtomKind::Text(text) => AtomKind::Text(text.clone()),
AtomKind::Image(image) => AtomKind::Image(image.clone()),
AtomKind::Closure(_) => {
log::warn!("Cannot clone atom closures");
AtomKind::Empty
}
AtomKind::Paint(paint) => AtomKind::Paint(Arc::clone(paint)),
AtomKind::Widget(layout) => AtomKind::Widget(layout.clone()),
AtomKind::Container(container) => AtomKind::Container(container.clone()),
}
}
}
impl Debug for AtomKind<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
AtomKind::Empty => write!(f, "AtomKind::Empty"),
AtomKind::Text(text) => write!(f, "AtomKind::Text({text:?})"),
AtomKind::Image(image) => write!(f, "AtomKind::Image({image:?})"),
AtomKind::Closure(_) => write!(f, "AtomKind::Closure(<closure>)"),
AtomKind::Paint(_) => write!(f, "AtomKind::Paint(<closure>)"),
AtomKind::Widget(_) => write!(f, "AtomKind::Widget(<widget>)"),
AtomKind::Container(_) => write!(f, "AtomKind::Container(<container>)"),
}
}
}
impl<'a> AtomKind<'a> {
pub fn text(text: impl Into<WidgetText>) -> Self {
AtomKind::Text(text.into())
}
pub fn image(image: impl Into<Image<'a>>) -> Self {
AtomKind::Image(image.into())
}
pub fn paint(func: impl Fn(&Ui, AtomPaintArgs) + 'a) -> Self {
AtomKind::Paint(Arc::new(func))
}
pub fn closure(func: impl FnOnce(&Ui, IntoSizedArgs) -> IntoSizedResult<'static> + 'a) -> Self {
AtomKind::Closure(Box::new(func))
}
pub fn into_sized(
self,
ui: &Ui,
IntoSizedArgs {
available_size,
wrap_mode,
fallback_font,
}: IntoSizedArgs,
) -> IntoSizedResult<'a> {
match self {
AtomKind::Text(text) => {
let galley = text.into_galley(ui, Some(wrap_mode), available_size.x, fallback_font);
IntoSizedResult {
intrinsic_size: galley.intrinsic_size(),
sized: SizedAtomKind::Text(galley),
}
}
AtomKind::Image(image) => {
let size = image.load_and_calc_size(ui, available_size);
let size = size.unwrap_or(Vec2::ZERO);
IntoSizedResult {
intrinsic_size: size,
sized: SizedAtomKind::Image { image, size },
}
}
AtomKind::Empty => IntoSizedResult {
intrinsic_size: Vec2::ZERO,
sized: SizedAtomKind::Empty { size: None },
},
AtomKind::Closure(func) => func(
ui,
IntoSizedArgs {
available_size,
wrap_mode,
fallback_font,
},
),
AtomKind::Paint(paint) => IntoSizedResult {
intrinsic_size: Vec2::ZERO,
sized: SizedAtomKind::Paint {
paint,
size: Vec2::ZERO,
},
},
AtomKind::Widget(layout) => {
let sized = layout.measure(ui, available_size);
IntoSizedResult {
intrinsic_size: sized.intrinsic_size,
sized: SizedAtomKind::Widget(Box::new(sized)),
}
}
AtomKind::Container(layout) => {
let sized = layout.measure(ui, available_size);
IntoSizedResult {
intrinsic_size: sized.intrinsic_size,
sized: SizedAtomKind::Container(Box::new(sized)),
}
}
}
}
}
impl<'a> From<ImageSource<'a>> for AtomKind<'a> {
fn from(value: ImageSource<'a>) -> Self {
AtomKind::Image(value.into())
}
}
impl<'a> From<Image<'a>> for AtomKind<'a> {
fn from(value: Image<'a>) -> Self {
AtomKind::Image(value)
}
}
impl<T> From<T> for AtomKind<'_>
where
T: Into<WidgetText>,
{
fn from(value: T) -> Self {
AtomKind::Text(value.into())
}
}
impl<'a> From<WidgetAtom<'a>> for AtomKind<'a> {
fn from(layout: WidgetAtom<'a>) -> Self {
AtomKind::Widget(Box::new(layout))
}
}
impl<'a> From<ContainerAtom<'a>> for AtomKind<'a> {
fn from(container: ContainerAtom<'a>) -> Self {
AtomKind::Container(Box::new(container))
}
}