use roost_ui::events::{TickEvent, OnEvent, MouseState, Event, MouseEvent};
use roost_ui::drawable::{Image, Color, Shape, ShapeType};
use roost_ui::{Context, Component, resources};
use crate::components::{Icon, Circle};
use roost_ui::layouts::{Stack, Offset, Size, Padding};
use crate::utils::Callback;
use crate::plugin::PelicanUI;
#[derive(Component)]
pub struct Avatar {
_layout: Stack,
_avatar: PrimaryAvatar,
_flair: Option<Flair>,
#[skip] _size: AvatarSize,
#[skip] on_click: Option<Callback>,
#[skip] pub content: AvatarContent,
#[skip] pub flair: Option<(String, AvatarIconStyle)>,
#[skip] pub outline: bool,
}
impl std::fmt::Debug for Avatar {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Avatar...")
}
}
impl Avatar {
pub fn new(
ctx: &mut Context,
content: AvatarContent,
flair: Option<(&str, AvatarIconStyle)>,
outline: bool,
size: AvatarSize,
on_click: Option<Callback>
) -> Self {
Avatar {
_layout: Stack(Offset::End, Offset::End, Size::Fit, Size::Fit, Padding::default()),
_avatar: PrimaryAvatar::new(ctx, content.clone(), outline, size),
_flair: flair.map(|(name, style)| Flair::new(ctx, name, style, size)),
_size: size,
on_click,
content,
flair: flair.map(|(n, s)| (n.to_string(), s)),
outline,
}
}
}
impl OnEvent for Avatar {
fn on_event(&mut self, ctx: &mut Context, mut event: Box<dyn Event>) -> Vec<Box<dyn Event>> {
if let Some(MouseEvent{state: MouseState::Pressed, position: Some(_)}) = event.as_any_mut().downcast_mut::<MouseEvent>() {
if let Some(on_click) = &mut self.on_click {
ctx.hardware.haptic();
(on_click)(ctx)
}
} else if event.as_any().downcast_ref::<TickEvent>().is_some() {
let (circle_icon, image) = match &self.content {
AvatarContent::Image(image) => (None, Some(Image{shape: ShapeType::Ellipse(0.0, (self._size.get(), self._size.get()), 0.0), image: image.clone(), color: None})),
AvatarContent::Icon(name, style) => (Some(AvatarIcon::new(ctx, name, *style, self._size.get())), None)
};
self._avatar.1 = circle_icon;
self._avatar.2 = image;
self._avatar.3 = self.outline.then(|| Circle::new(self._size.get(), Color::BLACK, true));
self._flair = self.flair.clone().map(|(name, style)| Flair::new(ctx, &name, style, self._size));
}
vec![event]
}
}
#[derive(Component, Debug)]
struct PrimaryAvatar(Stack, Option<AvatarIcon>, Option<Image>, Option<Shape>);
impl OnEvent for PrimaryAvatar {}
impl PrimaryAvatar {
fn new(ctx: &mut Context, content: AvatarContent, outline: bool, size: AvatarSize) -> Self {
let (circle_icon, image) = match content {
AvatarContent::Image(image) => (None, Some(Image{shape: ShapeType::Ellipse(0.0, (size.get(), size.get()), 0.0), image, color: None})),
AvatarContent::Icon(name, style) => (Some(AvatarIcon::new(ctx, &name, style, size.get())), None)
};
PrimaryAvatar(
Stack(Offset::Center, Offset::Center, Size::Fit, Size::Fit, Padding::default()),
circle_icon, image, outline.then(|| Circle::new(size.get(), Color::BLACK, true)),
)
}
}
#[derive(Debug, Component)]
struct AvatarIcon(Stack, Shape, Image);
impl OnEvent for AvatarIcon {}
impl AvatarIcon {
fn new(ctx: &mut Context, name: &str, style: AvatarIconStyle, size: f32) -> Self {
let icon_size = size * 0.75;
let (background, icon_color) = style.get(ctx);
AvatarIcon(
Stack::center(),
Circle::new(size - 2.0, background, false),
Icon::new(ctx, name, Some(icon_color), icon_size)
)
}
}
#[derive(Debug, Component)]
struct Flair(Stack, AvatarIcon, Shape);
impl OnEvent for Flair {}
impl Flair {
fn new(ctx: &mut Context, name: &str, style: AvatarIconStyle, size: AvatarSize) -> Self {
Flair(
Stack::center(),
AvatarIcon::new(ctx, name, style, size.get() / 3.0),
Circle::new(size.get() / 3.0, Color::BLACK, true)
)
}
}
#[derive(Debug, Clone)]
pub enum AvatarContent {
Icon(String, AvatarIconStyle),
Image(resources::Image)
}
#[derive(Debug, Copy, Clone)]
pub enum AvatarIconStyle {
Primary,
Secondary,
Brand,
Success,
Warning,
Danger,
}
impl AvatarIconStyle {
fn get(&self, ctx: &mut Context) -> (Color, Color) {
let colors = ctx.get::<PelicanUI>().get().0.theme().colors;
match self {
AvatarIconStyle::Primary => (colors.text.heading, colors.background.primary),
AvatarIconStyle::Secondary => (colors.background.secondary, colors.text.secondary),
AvatarIconStyle::Brand => (colors.brand, Color::WHITE),
AvatarIconStyle::Success => (colors.status.success, Color::WHITE),
AvatarIconStyle::Warning => (colors.status.warning, Color::WHITE),
AvatarIconStyle::Danger => (colors.status.danger, Color::WHITE),
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum AvatarSize {
Xxl,
Xl,
Lg,
Md,
Sm,
Xs
}
impl AvatarSize {
pub fn get(&self) -> f32 {
match self {
AvatarSize::Xxl => 128.0,
AvatarSize::Xl => 96.0,
AvatarSize::Lg => 64.0,
AvatarSize::Md => 48.0,
AvatarSize::Sm => 32.0,
AvatarSize::Xs => 24.0
}
}
}