use roost::{Component, Context, drawables};
use roost::events::{Event, OnEvent};
use roost::drawable::{Drawable, Align};
use roost::emitters;
use crate::interactions;
use crate::components::{TextStyle, Text, Icon};
use crate::components::avatar::{Avatar, AvatarContent, AvatarSize};
use crate::components::button::{Button, ButtonStyle, ButtonSize, ButtonWidth, IconButton};
use roost::layouts::{Stack, Offset};
use crate::plugin::PelicanUI;
pub enum PelicanError {
Err(String, Option<Box<dyn AppPage>>),
InvalidPage(Option<Box<dyn AppPage>>),
NoOutlet
}
impl From<String> for PelicanError {
fn from(s: String) -> PelicanError {
PelicanError::Err(s, None)
}
}
pub trait AppPage: Drawable + std::fmt::Debug + 'static {
fn navigate(self: Box<Self>, ctx: &mut Context, index: usize)
-> Result<Box<dyn AppPage>, PelicanError>;
fn has_navigator(&self) -> bool {true}
}
#[derive(Debug, Clone)]
pub struct NavigateEvent(pub usize);
impl Event for NavigateEvent {
fn pass(self: Box<Self>, _ctx: &mut Context, children: &Vec<((f32, f32), (f32, f32))>) -> Vec<Option<Box<dyn Event>>> {
children.iter().map(|_| Some(self.clone() as Box<dyn Event>)).collect()
}
}
pub type PageBuilder = Box<dyn FnMut(&mut Context) -> Box<dyn AppPage>>;
pub struct RootInfo {
pub(crate) icon: &'static str,
pub(crate) label: String,
pub(crate) avatar: Option<AvatarContent>,
pub(crate) get_page: Option<PageBuilder>
}
impl RootInfo {
pub fn icon(icon: &'static str, label: &str, get_page: impl FnMut(&mut Context) -> Box<dyn AppPage> + 'static) -> Self {
RootInfo {
icon,
label: label.to_string(),
avatar: None,
get_page: Some(Box::new(get_page))
}
}
pub fn avatar(avatar: AvatarContent, label: &str, get_page: impl FnMut(&mut Context) -> Box<dyn AppPage> + 'static) -> Self {
RootInfo {
icon: "profile",
label: label.to_string(),
avatar: Some(avatar),
get_page: Some(Box::new(get_page))
}
}
}
#[derive(Debug, Component)]
pub struct NavigatorSelectable(Stack, emitters::Selectable<interactions::Selectable>);
impl OnEvent for NavigatorSelectable {}
impl NavigatorSelectable {
pub fn desktop_icon(ctx: &mut Context, icon: &'static str, label: &str, on_click: impl FnMut(&mut Context) + 'static, is_selected: bool, group_id: uuid::Uuid) -> Self {
let colors = ctx.get::<PelicanUI>().get().0.theme().colors.button.ghost;
let [default, selected] = [colors.default, colors.pressed].map(|colors| {
let font_size = ButtonSize::Large.font();
let icon_size = ButtonSize::Large.icon();
let text = Text::new(ctx, label, font_size, TextStyle::Label(colors.label), Align::Left, None);
let icon = Icon::new(ctx, icon, colors.label, icon_size);
Button::new(drawables![icon, text], ButtonSize::Large, ButtonWidth::Fill, Offset::Start, colors.background, colors.outline)
});
NavigatorSelectable(Stack::default(), interactions::Selectable::new(default, selected, is_selected, on_click, group_id))
}
pub fn desktop_avatar(ctx: &mut Context, avatar: AvatarContent, label: &str, on_click: impl FnMut(&mut Context) + 'static, is_selected: bool, group_id: uuid::Uuid) -> Self {
let colors = ctx.get::<PelicanUI>().get().0.theme().colors.button.ghost;
let [default, selected] = [colors.default, colors.pressed].map(|colors| {
let font_size = ButtonSize::Large.font();
let text = Text::new(ctx, label, font_size, TextStyle::Label(colors.label), Align::Left, None);
let avatar = Avatar::new(ctx, avatar.clone(), None, false, AvatarSize::Xs, None);
Button::new(drawables![avatar, text], ButtonSize::Large, ButtonWidth::Fill, Offset::Start, colors.background, colors.outline)
});
NavigatorSelectable(Stack::default(), interactions::Selectable::new(default, selected, is_selected, on_click, group_id))
}
pub fn mobile(ctx: &mut Context, icon: &'static str, on_click: impl FnMut(&mut Context) + 'static, is_selected: bool, group_id: uuid::Uuid) -> Self {
let colors = ctx.get::<PelicanUI>().get().0.theme().colors.button.ghost;
let [default, selected] = [colors.disabled, colors.default].map(|colors| {
IconButton::new(ctx, icon, ButtonStyle::Ghost, ButtonSize::Large, colors.background, colors.outline, colors.label)
});
NavigatorSelectable(Stack::default(), interactions::Selectable::new(default, selected, is_selected, on_click, group_id))
}
}
#[derive(Debug, Clone)]
pub struct NavigatorSelect(pub uuid::Uuid);
impl Event for NavigatorSelect {
fn pass(self: Box<Self>, _ctx: &mut Context, children: &Vec<((f32, f32), (f32, f32))>) -> Vec<Option<Box<dyn Event>>> {
children.iter().map(|_| Some(self.clone() as Box<dyn Event>)).collect()
}
}
#[derive(Debug, Clone)]
pub struct NavigatorEvent(pub usize);
impl Event for NavigatorEvent {
fn pass(self: Box<Self>, _ctx: &mut Context, children: &Vec<((f32, f32), (f32, f32))>) -> Vec<Option<Box<dyn Event>>> {
children.iter().map(|_| Some(self.clone() as Box<dyn Event>)).collect()
}
}
#[macro_export]
macro_rules! page {
($result:expr, $self:expr) => {
$result
.map(|p| Box::new(p) as Box<dyn AppPage>)
.map_err(|e| PelicanError::Err(e, Some($self)))
};
}