1use image::RgbaImage;
2
3use super::*;
4use crate::component::Component;
5use anyhow::{Context, Error, Result};
6use std::pin::Pin;
7
8type RgbaImageFuture = Pin<Box<dyn Future<Output = Result<RgbaImage>>>>;
9type DataFuture = Pin<Box<dyn Future<Output = Result<Vec<u8>>>>>;
10
11#[derive(Default)]
13pub struct StyleBuilder {
14 pub(crate) images: HashMap<String, RgbaImageFuture>,
15 pub(crate) patches: HashMap<String, RgbaImageFuture>,
16 pub(crate) fonts: HashMap<String, DataFuture>,
17 pub(crate) rule_tree: tree::RuleTreeBuilder,
18}
19
20#[derive(Debug)]
22pub struct ImageId(pub(crate) String);
23#[derive(Debug)]
25pub struct PatchId(pub(crate) String);
26#[derive(Debug)]
28pub struct FontId(pub(crate) String);
29
30pub struct RuleBuilder {
32 selector: Vec<Selector>,
33 declarations: Vec<Declaration<ImageId, PatchId, FontId>>,
34}
35
36impl StyleBuilder {
37 fn base(foreground: Color, background: Color, primary: Color) -> Self {
38 Self::default()
39 .rule(RuleBuilder::new("*").color(foreground))
40 .rule(
41 RuleBuilder::new("button")
42 .padding_all(5.0)
43 .margin_all(5.0)
44 .background_color(background),
45 )
46 .rule(RuleBuilder::new("button:hover").background_color(background.blend(primary, 0.5)))
47 .rule(RuleBuilder::new("button:pressed").background_color(primary))
48 .rule(
49 RuleBuilder::new("dropdown")
50 .background_color(background)
51 .color(background.blend(primary, 0.5))
52 .padding_all(5.0)
53 .margin_all(5.0),
54 )
55 .rule(
56 RuleBuilder::new("input")
57 .width(300.0)
58 .background_color(Color::white())
59 .color(Color::black())
60 .padding_all(5.0)
61 .margin_all(5.0),
62 )
63 .rule(RuleBuilder::new("layers").fill_width().fill_height())
64 .rule(
65 RuleBuilder::new("menu")
66 .background_color(background)
67 .color(background.blend(primary, 0.5))
68 .padding_all(5.0),
69 )
70 .rule(RuleBuilder::new("spacer").fill_width().fill_height())
71 .rule(
72 RuleBuilder::new("window")
73 .background_color(background.blend(foreground, 0.2))
74 .padding_all(2.0),
75 )
76 .rule(RuleBuilder::new("window > *:nth-child(0)").background_color(background.blend(primary, 0.2)))
77 }
78
79 pub fn rule(mut self, builder: RuleBuilder) -> Self {
81 self.rule_tree.insert(builder.selector.as_slice(), builder.declarations);
82 self
83 }
84
85 pub fn scope<S: AsRef<str>>(mut self, selector: S) -> Self {
87 let mut old = std::mem::take(&mut self.rule_tree);
88
89 let selector = parse_selectors(tokenize(selector.as_ref().to_string()).unwrap()).unwrap();
90 if let Some(new_root) = selector.as_slice().last() {
91 old.selector = new_root.clone();
92 }
93 self.rule_tree.select(selector.as_slice()).merge(old);
94
95 self
96 }
97
98 pub fn merge(mut self, builder: StyleBuilder) -> Self {
100 self.images.extend(builder.images);
101 self.patches.extend(builder.patches);
102 self.fonts.extend(builder.fonts);
103 self.rule_tree.merge(builder.rule_tree);
104 self
105 }
106
107 pub fn component<C: Component>(mut self) -> Self {
109 let mut builder = C::style();
110 self.images.extend(builder.images);
111 self.patches.extend(builder.patches);
112 self.fonts.extend(builder.fonts);
113 let name = std::any::type_name::<C>().to_string();
114 builder.rule_tree.selector = Selector::Widget(SelectorWidget::Some(name.clone()));
115 self.rule_tree
116 .select(&[Selector::Widget(SelectorWidget::Some(name))])
117 .merge(builder.rule_tree);
118 self
119 }
120
121 pub async fn from_read_fn<P, R>(path: P, read: R) -> anyhow::Result<Self>
124 where
125 P: AsRef<Path>,
126 R: ReadFn,
127 {
128 let text = String::from_utf8(read.read(path.as_ref()).await?).unwrap();
129 Ok(parse(tokenize(text)?, read).await?)
130 }
131
132 pub fn from_file<P>(path: P) -> anyhow::Result<Self>
135 where
136 P: AsRef<Path>,
137 {
138 futures::executor::block_on(Self::from_read_fn(path, |path: &Path| {
139 std::future::ready(std::fs::read(path))
140 }))
141 }
142
143 pub fn load_image(
146 &mut self,
147 key: impl Into<String>,
148 load: impl FnOnce() -> Result<RgbaImage> + 'static,
149 ) -> ImageId {
150 self.load_image_async(key, async move { load() })
151 }
152
153 pub fn load_patch(
156 &mut self,
157 key: impl Into<String>,
158 load: impl FnOnce() -> Result<RgbaImage> + 'static,
159 ) -> PatchId {
160 self.load_patch_async(key, async move { load() })
161 }
162
163 pub fn load_font(&mut self, key: impl Into<String>, load: impl FnOnce() -> Result<Vec<u8>> + 'static) -> FontId {
167 self.load_font_async(key, async move { load() })
168 }
169
170 pub fn load_image_async(
173 &mut self,
174 key: impl Into<String>,
175 fut: impl Future<Output = Result<RgbaImage>> + 'static,
176 ) -> ImageId {
177 let key = key.into();
178 if let std::collections::hash_map::Entry::Vacant(v) = self.images.entry(key.clone()) {
179 v.insert(Box::pin(fut));
180 }
181 ImageId(key)
182 }
183
184 pub fn load_patch_async(
187 &mut self,
188 key: impl Into<String>,
189 fut: impl Future<Output = Result<RgbaImage>> + 'static,
190 ) -> PatchId {
191 let key = key.into();
192 if let std::collections::hash_map::Entry::Vacant(v) = self.patches.entry(key.clone()) {
193 v.insert(Box::pin(fut));
194 }
195 PatchId(key)
196 }
197
198 pub fn load_font_async(
202 &mut self,
203 key: impl Into<String>,
204 fut: impl Future<Output = Result<Vec<u8>>> + 'static,
205 ) -> FontId {
206 let key = key.into();
207 if let std::collections::hash_map::Entry::Vacant(v) = self.fonts.entry(key.clone()) {
208 v.insert(Box::pin(fut));
209 }
210 FontId(key)
211 }
212
213 pub async fn build_async(mut self) -> Result<Style> {
216 self = Self::base(Color::white(), Color::rgb(0.3, 0.3, 0.3), Color::blue()).merge(self);
217
218 let mut cache = Cache::new(512);
219
220 let font = cache.load_font(include_bytes!("default_font.ttf").to_vec()).unwrap();
221
222 let mut images = HashMap::new();
223 for (key, value) in self.images {
224 images.insert(
225 key.clone(),
226 cache.load_image(
227 value
228 .await
229 .with_context(|| format!("Failed to load image \"{}\": ", key))?,
230 ),
231 );
232 }
233
234 let mut patches = HashMap::new();
235 for (key, value) in self.patches {
236 patches.insert(
237 key.clone(),
238 cache.load_patch(
239 value
240 .await
241 .with_context(|| format!("Failed to load 9 patch \"{}\": ", key))?,
242 ),
243 );
244 }
245
246 let mut fonts = HashMap::new();
247 for (key, value) in self.fonts {
248 let load = async { Result::<_, Error>::Ok(cache.load_font(value.await?)?) };
249 fonts.insert(
250 key.clone(),
251 load.await
252 .with_context(|| format!("Failed to load font \"{}\": ", key))?,
253 );
254 }
255
256 Ok(Style {
257 cache: Arc::new(Mutex::new(cache)),
258 resolved: Default::default(),
259 default: Stylesheet {
260 background: Background::None,
261 font,
262 color: Color::white(),
263 padding: Rectangle::zero(),
264 margin: Rectangle::zero(),
265 text_size: 16.0,
266 text_wrap: TextWrap::NoWrap,
267 width: Size::Shrink,
268 height: Size::Shrink,
269 direction: Direction::LeftToRight,
270 align_horizontal: Align::Begin,
271 align_vertical: Align::Begin,
272 flags: Vec::new(),
273 },
274 rule_tree: self.rule_tree.build(&images, &patches, &fonts),
275 })
276 }
277
278 pub fn build(self) -> Result<Style> {
281 futures::executor::block_on(self.build_async())
282 }
283}
284
285impl TryInto<Style> for StyleBuilder {
286 type Error = Error;
287
288 fn try_into(self) -> Result<Style> {
289 self.build()
290 }
291}
292
293impl RuleBuilder {
294 pub fn new<S: AsRef<str>>(selector: S) -> Self {
306 Self {
307 selector: parse_selectors(tokenize(selector.as_ref().to_string()).unwrap()).unwrap(),
308 declarations: Vec::new(),
309 }
310 }
311 pub fn background_none(mut self) -> Self {
313 self.declarations.push(Declaration::BackgroundNone);
314 self
315 }
316 pub fn background_color(mut self, color: Color) -> Self {
318 self.declarations.push(Declaration::BackgroundColor(color));
319 self
320 }
321 pub fn background_image(mut self, image_data: ImageId, color: Color) -> Self {
323 self.declarations.push(Declaration::BackgroundImage(image_data, color));
324 self
325 }
326 pub fn background_patch(mut self, patch: PatchId, color: Color) -> Self {
328 self.declarations.push(Declaration::BackgroundPatch(patch, color));
329 self
330 }
331 pub fn font(mut self, value: FontId) -> Self {
333 self.declarations.push(Declaration::Font(value));
334 self
335 }
336 pub fn color(mut self, value: Color) -> Self {
338 self.declarations.push(Declaration::Color(value));
339 self
340 }
341 pub fn padding(mut self, value: Rectangle) -> Self {
343 self.declarations.push(Declaration::Padding(value));
344 self
345 }
346 pub fn padding_all(self, value: f32) -> Self {
348 self.padding(Rectangle {
349 left: value,
350 top: value,
351 right: value,
352 bottom: value,
353 })
354 }
355 pub fn padding_horizontal(self, value: f32) -> Self {
357 self.padding_left(value).padding_right(value)
358 }
359 pub fn padding_vertical(self, value: f32) -> Self {
361 self.padding_top(value).padding_bottom(value)
362 }
363 pub fn padding_left(mut self, value: f32) -> Self {
365 self.declarations.push(Declaration::PaddingLeft(value));
366 self
367 }
368 pub fn padding_right(mut self, value: f32) -> Self {
370 self.declarations.push(Declaration::PaddingRight(value));
371 self
372 }
373 pub fn padding_top(mut self, value: f32) -> Self {
375 self.declarations.push(Declaration::PaddingTop(value));
376 self
377 }
378 pub fn padding_bottom(mut self, value: f32) -> Self {
380 self.declarations.push(Declaration::PaddingBottom(value));
381 self
382 }
383 pub fn margin(mut self, value: Rectangle) -> Self {
385 self.declarations.push(Declaration::Margin(value));
386 self
387 }
388 pub fn margin_all(self, value: f32) -> Self {
390 self.margin(Rectangle {
391 left: value,
392 top: value,
393 right: value,
394 bottom: value,
395 })
396 }
397 pub fn margin_horizontal(self, value: f32) -> Self {
399 self.margin_left(value).margin_right(value)
400 }
401 pub fn margin_vertical(self, value: f32) -> Self {
403 self.margin_top(value).margin_bottom(value)
404 }
405 pub fn margin_left(mut self, value: f32) -> Self {
407 self.declarations.push(Declaration::MarginLeft(value));
408 self
409 }
410 pub fn margin_right(mut self, value: f32) -> Self {
412 self.declarations.push(Declaration::MarginRight(value));
413 self
414 }
415 pub fn margin_top(mut self, value: f32) -> Self {
417 self.declarations.push(Declaration::MarginTop(value));
418 self
419 }
420 pub fn margin_bottom(mut self, value: f32) -> Self {
422 self.declarations.push(Declaration::MarginBottom(value));
423 self
424 }
425 pub fn text_size(mut self, value: f32) -> Self {
427 self.declarations.push(Declaration::TextSize(value));
428 self
429 }
430 pub fn text_wrap(mut self, value: TextWrap) -> Self {
432 self.declarations.push(Declaration::TextWrap(value));
433 self
434 }
435 pub fn width(mut self, value: impl Into<Size>) -> Self {
437 self.declarations.push(Declaration::Width(value.into()));
438 self
439 }
440 pub fn fill_width(mut self) -> Self {
442 self.declarations.push(Declaration::Width(Size::Fill(1)));
443 self
444 }
445 pub fn height(mut self, value: impl Into<Size>) -> Self {
447 self.declarations.push(Declaration::Height(value.into()));
448 self
449 }
450 pub fn fill_height(mut self) -> Self {
452 self.declarations.push(Declaration::Height(Size::Fill(1)));
453 self
454 }
455 pub fn layout_direction(mut self, value: Direction) -> Self {
457 self.declarations.push(Declaration::LayoutDirection(value));
458 self
459 }
460 pub fn align_horizontal(mut self, value: Align) -> Self {
462 self.declarations.push(Declaration::AlignHorizontal(value));
463 self
464 }
465 pub fn align_vertical(mut self, value: Align) -> Self {
467 self.declarations.push(Declaration::AlignVertical(value));
468 self
469 }
470 pub fn add_flag(mut self, value: String) -> Self {
472 self.declarations.push(Declaration::AddFlag(value));
473 self
474 }
475 pub fn remove_flag(mut self, value: String) -> Self {
477 self.declarations.push(Declaration::RemoveFlag(value));
478 self
479 }
480}