use image::RgbaImage;
use super::*;
use crate::component::Component;
use anyhow::{Context, Error, Result};
use std::pin::Pin;
type RgbaImageFuture = Pin<Box<dyn Future<Output = Result<RgbaImage>>>>;
type DataFuture = Pin<Box<dyn Future<Output = Result<Vec<u8>>>>>;
#[derive(Default)]
pub struct StyleBuilder {
pub(crate) images: HashMap<String, RgbaImageFuture>,
pub(crate) patches: HashMap<String, RgbaImageFuture>,
pub(crate) fonts: HashMap<String, DataFuture>,
pub(crate) rule_tree: tree::RuleTreeBuilder,
}
#[derive(Debug)]
pub struct ImageId(pub(crate) String);
#[derive(Debug)]
pub struct PatchId(pub(crate) String);
#[derive(Debug)]
pub struct FontId(pub(crate) String);
pub struct RuleBuilder {
selector: Vec<Selector>,
declarations: Vec<Declaration<ImageId, PatchId, FontId>>,
}
impl StyleBuilder {
fn base(foreground: Color, background: Color, primary: Color) -> Self {
Self::default()
.rule(RuleBuilder::new("*").color(foreground))
.rule(
RuleBuilder::new("button")
.padding_all(5.0)
.margin_all(5.0)
.background_color(background),
)
.rule(RuleBuilder::new("button:hover").background_color(background.blend(primary, 0.5)))
.rule(RuleBuilder::new("button:pressed").background_color(primary))
.rule(
RuleBuilder::new("dropdown")
.background_color(background)
.color(background.blend(primary, 0.5))
.padding_all(5.0)
.margin_all(5.0),
)
.rule(
RuleBuilder::new("input")
.width(300.0)
.background_color(Color::white())
.color(Color::black())
.padding_all(5.0)
.margin_all(5.0),
)
.rule(RuleBuilder::new("layers").fill_width().fill_height())
.rule(
RuleBuilder::new("menu")
.background_color(background)
.color(background.blend(primary, 0.5))
.padding_all(5.0),
)
.rule(RuleBuilder::new("spacer").fill_width().fill_height())
.rule(
RuleBuilder::new("window")
.background_color(background.blend(foreground, 0.2))
.padding_all(2.0),
)
.rule(RuleBuilder::new("window > *:nth-child(0)").background_color(background.blend(primary, 0.2)))
}
pub fn rule(mut self, builder: RuleBuilder) -> Self {
self.rule_tree.insert(builder.selector.as_slice(), builder.declarations);
self
}
pub fn scope<S: AsRef<str>>(mut self, selector: S) -> Self {
let mut old = std::mem::take(&mut self.rule_tree);
let selector = parse_selectors(tokenize(selector.as_ref().to_string()).unwrap()).unwrap();
if let Some(new_root) = selector.as_slice().last() {
old.selector = new_root.clone();
}
self.rule_tree.select(selector.as_slice()).merge(old);
self
}
pub fn merge(mut self, builder: StyleBuilder) -> Self {
self.images.extend(builder.images);
self.patches.extend(builder.patches);
self.fonts.extend(builder.fonts);
self.rule_tree.merge(builder.rule_tree);
self
}
pub fn component<C: Component>(mut self) -> Self {
let mut builder = C::style();
self.images.extend(builder.images);
self.patches.extend(builder.patches);
self.fonts.extend(builder.fonts);
let name = std::any::type_name::<C>().to_string();
builder.rule_tree.selector = Selector::Widget(SelectorWidget::Some(name.clone()));
self.rule_tree
.select(&[Selector::Widget(SelectorWidget::Some(name))])
.merge(builder.rule_tree);
self
}
pub async fn from_read_fn<P, R>(path: P, read: R) -> anyhow::Result<Self>
where
P: AsRef<Path>,
R: ReadFn,
{
let text = String::from_utf8(read.read(path.as_ref()).await?).unwrap();
Ok(parse(tokenize(text)?, read).await?)
}
pub fn from_file<P>(path: P) -> anyhow::Result<Self>
where
P: AsRef<Path>,
{
futures::executor::block_on(Self::from_read_fn(path, |path: &Path| {
std::future::ready(std::fs::read(path))
}))
}
pub fn load_image(
&mut self,
key: impl Into<String>,
load: impl FnOnce() -> Result<RgbaImage> + 'static,
) -> ImageId {
self.load_image_async(key, async move { load() })
}
pub fn load_patch(
&mut self,
key: impl Into<String>,
load: impl FnOnce() -> Result<RgbaImage> + 'static,
) -> PatchId {
self.load_patch_async(key, async move { load() })
}
pub fn load_font(&mut self, key: impl Into<String>, load: impl FnOnce() -> Result<Vec<u8>> + 'static) -> FontId {
self.load_font_async(key, async move { load() })
}
pub fn load_image_async(
&mut self,
key: impl Into<String>,
fut: impl Future<Output = Result<RgbaImage>> + 'static,
) -> ImageId {
let key = key.into();
if let std::collections::hash_map::Entry::Vacant(v) = self.images.entry(key.clone()) {
v.insert(Box::pin(fut));
}
ImageId(key)
}
pub fn load_patch_async(
&mut self,
key: impl Into<String>,
fut: impl Future<Output = Result<RgbaImage>> + 'static,
) -> PatchId {
let key = key.into();
if let std::collections::hash_map::Entry::Vacant(v) = self.patches.entry(key.clone()) {
v.insert(Box::pin(fut));
}
PatchId(key)
}
pub fn load_font_async(
&mut self,
key: impl Into<String>,
fut: impl Future<Output = Result<Vec<u8>>> + 'static,
) -> FontId {
let key = key.into();
if let std::collections::hash_map::Entry::Vacant(v) = self.fonts.entry(key.clone()) {
v.insert(Box::pin(fut));
}
FontId(key)
}
pub async fn build_async(mut self) -> Result<Style> {
self = Self::base(Color::white(), Color::rgb(0.3, 0.3, 0.3), Color::blue()).merge(self);
let mut cache = Cache::new(512);
let font = cache.load_font(include_bytes!("default_font.ttf").to_vec()).unwrap();
let mut images = HashMap::new();
for (key, value) in self.images {
images.insert(
key.clone(),
cache.load_image(
value
.await
.with_context(|| format!("Failed to load image \"{}\": ", key))?,
),
);
}
let mut patches = HashMap::new();
for (key, value) in self.patches {
patches.insert(
key.clone(),
cache.load_patch(
value
.await
.with_context(|| format!("Failed to load 9 patch \"{}\": ", key))?,
),
);
}
let mut fonts = HashMap::new();
for (key, value) in self.fonts {
let load = async { Result::<_, Error>::Ok(cache.load_font(value.await?)?) };
fonts.insert(
key.clone(),
load.await
.with_context(|| format!("Failed to load font \"{}\": ", key))?,
);
}
Ok(Style {
cache: Arc::new(Mutex::new(cache)),
resolved: Default::default(),
default: Stylesheet {
background: Background::None,
font,
color: Color::white(),
padding: Rectangle::zero(),
margin: Rectangle::zero(),
text_size: 16.0,
text_wrap: TextWrap::NoWrap,
width: Size::Shrink,
height: Size::Shrink,
direction: Direction::LeftToRight,
align_horizontal: Align::Begin,
align_vertical: Align::Begin,
flags: Vec::new(),
},
rule_tree: self.rule_tree.build(&images, &patches, &fonts),
})
}
pub fn build(self) -> Result<Style> {
futures::executor::block_on(self.build_async())
}
}
impl TryInto<Style> for StyleBuilder {
type Error = Error;
fn try_into(self) -> Result<Style> {
self.build()
}
}
impl RuleBuilder {
pub fn new<S: AsRef<str>>(selector: S) -> Self {
Self {
selector: parse_selectors(tokenize(selector.as_ref().to_string()).unwrap()).unwrap(),
declarations: Vec::new(),
}
}
pub fn background_none(mut self) -> Self {
self.declarations.push(Declaration::BackgroundNone);
self
}
pub fn background_color(mut self, color: Color) -> Self {
self.declarations.push(Declaration::BackgroundColor(color));
self
}
pub fn background_image(mut self, image_data: ImageId, color: Color) -> Self {
self.declarations.push(Declaration::BackgroundImage(image_data, color));
self
}
pub fn background_patch(mut self, patch: PatchId, color: Color) -> Self {
self.declarations.push(Declaration::BackgroundPatch(patch, color));
self
}
pub fn font(mut self, value: FontId) -> Self {
self.declarations.push(Declaration::Font(value));
self
}
pub fn color(mut self, value: Color) -> Self {
self.declarations.push(Declaration::Color(value));
self
}
pub fn padding(mut self, value: Rectangle) -> Self {
self.declarations.push(Declaration::Padding(value));
self
}
pub fn padding_all(self, value: f32) -> Self {
self.padding(Rectangle {
left: value,
top: value,
right: value,
bottom: value,
})
}
pub fn padding_horizontal(self, value: f32) -> Self {
self.padding_left(value).padding_right(value)
}
pub fn padding_vertical(self, value: f32) -> Self {
self.padding_top(value).padding_bottom(value)
}
pub fn padding_left(mut self, value: f32) -> Self {
self.declarations.push(Declaration::PaddingLeft(value));
self
}
pub fn padding_right(mut self, value: f32) -> Self {
self.declarations.push(Declaration::PaddingRight(value));
self
}
pub fn padding_top(mut self, value: f32) -> Self {
self.declarations.push(Declaration::PaddingTop(value));
self
}
pub fn padding_bottom(mut self, value: f32) -> Self {
self.declarations.push(Declaration::PaddingBottom(value));
self
}
pub fn margin(mut self, value: Rectangle) -> Self {
self.declarations.push(Declaration::Margin(value));
self
}
pub fn margin_all(self, value: f32) -> Self {
self.margin(Rectangle {
left: value,
top: value,
right: value,
bottom: value,
})
}
pub fn margin_horizontal(self, value: f32) -> Self {
self.margin_left(value).margin_right(value)
}
pub fn margin_vertical(self, value: f32) -> Self {
self.margin_top(value).margin_bottom(value)
}
pub fn margin_left(mut self, value: f32) -> Self {
self.declarations.push(Declaration::MarginLeft(value));
self
}
pub fn margin_right(mut self, value: f32) -> Self {
self.declarations.push(Declaration::MarginRight(value));
self
}
pub fn margin_top(mut self, value: f32) -> Self {
self.declarations.push(Declaration::MarginTop(value));
self
}
pub fn margin_bottom(mut self, value: f32) -> Self {
self.declarations.push(Declaration::MarginBottom(value));
self
}
pub fn text_size(mut self, value: f32) -> Self {
self.declarations.push(Declaration::TextSize(value));
self
}
pub fn text_wrap(mut self, value: TextWrap) -> Self {
self.declarations.push(Declaration::TextWrap(value));
self
}
pub fn width(mut self, value: impl Into<Size>) -> Self {
self.declarations.push(Declaration::Width(value.into()));
self
}
pub fn fill_width(mut self) -> Self {
self.declarations.push(Declaration::Width(Size::Fill(1)));
self
}
pub fn height(mut self, value: impl Into<Size>) -> Self {
self.declarations.push(Declaration::Height(value.into()));
self
}
pub fn fill_height(mut self) -> Self {
self.declarations.push(Declaration::Height(Size::Fill(1)));
self
}
pub fn layout_direction(mut self, value: Direction) -> Self {
self.declarations.push(Declaration::LayoutDirection(value));
self
}
pub fn align_horizontal(mut self, value: Align) -> Self {
self.declarations.push(Declaration::AlignHorizontal(value));
self
}
pub fn align_vertical(mut self, value: Align) -> Self {
self.declarations.push(Declaration::AlignVertical(value));
self
}
pub fn add_flag(mut self, value: String) -> Self {
self.declarations.push(Declaration::AddFlag(value));
self
}
pub fn remove_flag(mut self, value: String) -> Self {
self.declarations.push(Declaration::RemoveFlag(value));
self
}
}