1use serde::{Deserialize, Serialize};
35use std::collections::HashMap;
36use std::str::FromStr;
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
40#[serde(untagged)]
41pub enum TokenValue {
42 Single {
44 value: String,
45 },
46 Adaptive {
48 light: String,
49 dark: String,
50 },
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct YggdrasilTokens {
56 pub color: HashMap<String, TokenValue>,
57 pub font: HashMap<String, TokenValue>,
58 pub spacing: HashMap<String, TokenValue>,
59 pub radius: HashMap<String, TokenValue>,
60 pub shadow: HashMap<String, TokenValue>,
61 pub border: HashMap<String, TokenValue>,
62 pub anim: HashMap<String, TokenValue>,
63 pub bifrost: HashMap<String, TokenValue>,
64 pub gungnir: HashMap<String, TokenValue>,
65 pub mjolnir: HashMap<String, TokenValue>,
66 pub accessibility: HashMap<String, TokenValue>,
67}
68
69impl YggdrasilTokens {
70 pub fn new() -> Self {
71 Self {
72 color: HashMap::new(),
73 font: HashMap::new(),
74 spacing: HashMap::new(),
75 radius: HashMap::new(),
76 shadow: HashMap::new(),
77 border: HashMap::new(),
78 anim: HashMap::new(),
79 bifrost: HashMap::new(),
80 gungnir: HashMap::new(),
81 mjolnir: HashMap::new(),
82 accessibility: HashMap::new(),
83 }
84 }
85
86 pub fn get_color(&self, key: &str, is_dark: bool) -> Option<String> {
88 self.color.get(key).and_then(|token| {
89 match token {
90 TokenValue::Single { value } => Some(value.clone()),
91 TokenValue::Adaptive { light, dark } => {
92 if is_dark { Some(dark.clone()) } else { Some(light.clone()) }
93 }
94 }
95 })
96 }
97
98 pub fn get<T: FromStr>(&self, category: &str, key: &str, is_dark: bool) -> Option<T> {
100 let map = match category {
101 "color" => &self.color,
102 "font" => &self.font,
103 "spacing" => &self.spacing,
104 "radius" => &self.radius,
105 "shadow" => &self.shadow,
106 "border" => &self.border,
107 "anim" => &self.anim,
108 "bifrost" => &self.bifrost,
109 "gungnir" => &self.gungnir,
110 "mjolnir" => &self.mjolnir,
111 "accessibility" => &self.accessibility,
112 _ => return None,
113 };
114
115 map.get(key).and_then(|token| {
116 match token {
117 TokenValue::Single { value } => value.parse().ok(),
118 TokenValue::Adaptive { light, dark } => {
119 let value = if is_dark { dark } else { light };
120 value.parse().ok()
121 }
122 }
123 })
124 }
125}
126
127pub trait View: Sized + Send {
128 type Body: View;
131
132 fn body(self) -> Self::Body;
133
134 fn render(&self, _renderer: &mut dyn Renderer, _rect: Rect) {}
137
138 fn modifier<M: ViewModifier>(self, m: M) -> ModifiedView<Self, M> {
140 ModifiedView::new(self, m)
141 }
142
143 fn bifrost(self, blur: f32, saturation: f32, opacity: f32) -> ModifiedView<Self, BifrostModifier> {
145 self.modifier(BifrostModifier { blur, saturation, opacity })
146 }
147
148 fn gungnir(self, color: impl Into<String>, radius: f32, intensity: f32) -> ModifiedView<Self, GungnirModifier> {
150 self.modifier(GungnirModifier { color: color.into(), radius, intensity })
151 }
152
153 fn mjolnir_slice(self, angle: f32, offset: f32) -> ModifiedView<Self, MjolnirSliceModifier> {
155 self.modifier(MjolnirSliceModifier { angle, offset })
156 }
157
158 fn mjolnir_shatter(self, pieces: u32, force: f32) -> ModifiedView<Self, MjolnirShatterModifier> {
160 self.modifier(MjolnirShatterModifier { pieces, force })
161 }
162
163 fn bifrost_bridge(self, id: impl Into<String>) -> ModifiedView<Self, BifrostBridgeModifier> {
165 self.modifier(BifrostBridgeModifier { id: id.into() })
166 }
167
168 fn background(self, color: [f32; 4]) -> ModifiedView<Self, BackgroundModifier> {
170 self.modifier(BackgroundModifier { color })
171 }
172
173 fn padding(self, amount: f32) -> ModifiedView<Self, PaddingModifier> {
175 self.modifier(PaddingModifier { amount })
176 }
177
178 fn on_appear<F: Fn() + Send + Sync + 'static>(self, action: F) -> ModifiedView<Self, LifecycleModifier> {
180 self.modifier(LifecycleModifier {
181 on_appear: Some(Arc::new(action)),
182 on_disappear: None,
183 })
184 }
185
186 fn on_disappear<F: Fn() + Send + Sync + 'static>(self, action: F) -> ModifiedView<Self, LifecycleModifier> {
188 self.modifier(LifecycleModifier {
189 on_appear: None,
190 on_disappear: Some(Arc::new(action)),
191 })
192 }
193
194 fn erase(self) -> AnyView where Self: 'static {
196 AnyView::new(self)
197 }
198}
199
200pub trait ErasedView: Send {
202 fn render_erased(&self, renderer: &mut dyn Renderer, rect: Rect);
203}
204
205impl<V: View + 'static> ErasedView for V {
206 fn render_erased(&self, renderer: &mut dyn Renderer, rect: Rect) {
207 self.render(renderer, rect);
208 }
209}
210
211pub struct AnyView {
213 inner: Box<dyn ErasedView>,
214}
215
216impl AnyView {
217 pub fn new<V: View + 'static>(view: V) -> Self {
218 Self { inner: Box::new(view) }
219 }
220}
221
222impl View for AnyView {
223 type Body = Never;
224 fn body(self) -> Self::Body { unreachable!() }
225
226 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
227 self.inner.render_erased(renderer, rect);
228 }
229}
230
231#[derive(Debug, Clone, PartialEq)]
235pub struct BifrostBridgeModifier {
236 pub id: String,
237}
238
239impl ViewModifier for BifrostBridgeModifier {
240 fn modify<V: View>(self, content: V) -> impl View {
241 ModifiedView::new(content, self)
242 }
243}
244
245#[derive(Debug, Clone, Copy, PartialEq)]
248pub struct MjolnirSliceModifier {
249 pub angle: f32,
250 pub offset: f32,
251}
252
253impl ViewModifier for MjolnirSliceModifier {
254 fn modify<V: View>(self, content: V) -> impl View {
255 ModifiedView::new(content, self)
256 }
257}
258
259#[derive(Debug, Clone, Copy, PartialEq)]
262pub struct MjolnirShatterModifier {
263 pub pieces: u32,
264 pub force: f32,
265}
266
267impl ViewModifier for MjolnirShatterModifier {
268 fn modify<V: View>(self, content: V) -> impl View {
269 ModifiedView::new(content, self)
270 }
271}
272
273#[derive(Debug, Clone, Copy, PartialEq)]
276pub struct BifrostModifier {
277 pub blur: f32,
278 pub saturation: f32,
279 pub opacity: f32,
280}
281
282impl ViewModifier for BifrostModifier {
283 fn modify<V: View>(self, content: V) -> impl View {
284 ModifiedView::new(content, self)
285 }
286
287 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
288 renderer.fill_rect(rect, [1.0, 1.0, 1.0, 0.1]);
290 }
291}
292
293#[derive(Debug, Clone, Copy, PartialEq)]
295pub struct BackgroundModifier {
296 pub color: [f32; 4],
297}
298
299impl ViewModifier for BackgroundModifier {
300 fn modify<V: View>(self, content: V) -> impl View {
301 ModifiedView::new(content, self)
302 }
303
304 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
305 renderer.fill_rect(rect, self.color);
306 }
307}
308
309#[derive(Debug, Clone, Copy, PartialEq)]
311pub struct PaddingModifier {
312 pub amount: f32,
313}
314
315impl ViewModifier for PaddingModifier {
316 fn modify<V: View>(self, content: V) -> impl View {
317 ModifiedView::new(content, self)
318 }
319
320 fn transform_rect(&self, rect: Rect) -> Rect {
321 Rect {
322 x: rect.x + self.amount,
323 y: rect.y + self.amount,
324 width: (rect.width - 2.0 * self.amount).max(0.0),
325 height: (rect.height - 2.0 * self.amount).max(0.0),
326 }
327 }
328}
329
330#[derive(Debug, Clone, PartialEq)]
333pub struct GungnirModifier {
334 pub color: String,
335 pub radius: f32,
336 pub intensity: f32,
337}
338
339impl ViewModifier for GungnirModifier {
340 fn modify<V: View>(self, content: V) -> impl View {
341 ModifiedView::new(content, self)
342 }
343
344 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
345 renderer.stroke_rect(rect, [0.0, 1.0, 1.0, self.intensity], self.radius / 10.0);
347 }
348}
349
350#[derive(Debug, Clone, PartialEq)]
352pub struct SleipnirModifier<T> {
353 pub target: T,
354 pub stiffness: f32,
355 pub damping: f32,
356}
357
358impl<T: Send + Sync + 'static + Clone> ViewModifier for SleipnirModifier<T> {
359 fn modify<V: View>(self, content: V) -> impl View {
360 ModifiedView::new(content, self)
361 }
362}
363
364#[derive(Clone)]
366pub struct LifecycleModifier {
367 pub on_appear: Option<Arc<dyn Fn() + Send + Sync>>,
368 pub on_disappear: Option<Arc<dyn Fn() + Send + Sync>>,
369}
370
371impl ViewModifier for LifecycleModifier {
372 fn modify<V: View>(self, content: V) -> impl View {
373 ModifiedView::new(content, self)
374 }
375}
376
377#[doc(hidden)]
379pub enum Never {}
380
381impl View for Never {
382 type Body = Never;
383 fn body(self) -> Never {
384 unreachable!()
385 }
386}
387
388pub struct ModifiedView<V, M> {
392 view: V,
393 modifier: M,
394}
395
396 impl<V: View, M: ViewModifier> ModifiedView<V, M> {
397 #[doc(hidden)]
398 pub fn new(view: V, modifier: M) -> Self {
399 Self { view, modifier }
400 }
401 }
402
403 impl<V: View, M: ViewModifier> View for ModifiedView<V, M> {
404 type Body = ModifiedView<V::Body, M>;
405
406 fn body(self) -> Self::Body {
407 ModifiedView {
408 view: self.view.body(),
409 modifier: self.modifier.clone(),
410 }
411 }
412
413 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
414 self.modifier.render(renderer, rect);
415 let child_rect = self.modifier.transform_rect(rect);
416 self.view.render(renderer, child_rect);
417 }
418 }
419
420
421 pub trait ViewModifier: Send + Clone {
422 fn modify<V: View>(self, content: V) -> impl View;
423 fn render(&self, renderer: &mut dyn Renderer, _rect: Rect) {}
424 fn transform_rect(&self, rect: Rect) -> Rect { rect }
425 }
426
427 pub trait Renderer {
434 fn begin_frame(&mut self);
435 fn end_frame(&mut self);
436
437 fn fill_rect(&mut self, rect: Rect, color: [f32; 4]);
438 fn stroke_rect(&mut self, rect: Rect, color: [f32; 4], stroke_width: f32);
439 fn fill_rounded_rect(&mut self, rect: Rect, radius: f32, color: [f32; 4]);
440 fn draw_text(&mut self, text: &str, x: f32, y: f32, size: f32, color: [f32; 4]);
441
442 fn draw_texture(&mut self, texture_id: u32, rect: Rect);
444
445 fn draw_image(&mut self, image_name: &str, rect: Rect);
447 }
448
449
450
451use std::sync::Arc;
452
453#[derive(Clone)]
455pub struct State<T: Clone + Send + Sync + 'static> {
456 value: Arc<std::sync::RwLock<T>>,
457 subscribers: Arc<std::sync::RwLock<Vec<Box<dyn FnMut(&T) + Send + Sync>>>>,
458}
459
460impl<T: Clone + Send + Sync + 'static> State<T> {
461 pub fn new(value: T) -> Self {
463 Self {
464 value: Arc::new(std::sync::RwLock::new(value)),
465 subscribers: Arc::new(std::sync::RwLock::new(Vec::new())),
466 }
467 }
468
469 pub fn get(&self) -> T {
471 self.value.read().unwrap().clone()
472 }
473
474 pub fn set(&self, value: T) {
476 *self.value.write().unwrap() = value;
477 let mut subscribers = self.subscribers.write().unwrap();
479 for subscriber in subscribers.iter_mut() {
480 subscriber(&self.get());
481 }
482 }
483
484 pub fn subscribe<F: FnMut(&T) + Send + Sync + 'static>(&self, callback: F) {
486 self.subscribers.write().unwrap().push(Box::new(callback));
487 }
488}
489
490#[derive(Clone)]
492pub struct Binding<T: Clone + Send + Sync + 'static> {
493 state: Arc<std::sync::RwLock<T>>,
494}
495
496impl<T: Clone + Send + Sync + 'static> Binding<T> {
497 pub fn from_state(state: &State<T>) -> Self {
499 Self {
500 state: state.value.clone(),
501 }
502 }
503
504 pub fn get(&self) -> T {
506 self.state.read().unwrap().clone()
507 }
508
509 pub fn set(&self, value: T) {
511 *self.state.write().unwrap() = value;
512 }
513}
514
515use std::any::TypeId;
516use std::sync::{Mutex, OnceLock};
517
518pub(crate) static ENVIRONMENT: OnceLock<Mutex<HashMap<TypeId, Box<dyn std::any::Any + Send + Sync>>>> = OnceLock::new();
520
521pub trait EnvKey: 'static + Send + Sync {
525 type Value: Clone + Send + Sync + 'static;
527
528 fn default_value() -> Self::Value;
530}
531
532pub struct YggdrasilKey;
534
535impl EnvKey for YggdrasilKey {
536 type Value = YggdrasilTokens;
537 fn default_value() -> Self::Value {
538 default_tokens()
539 }
540}
541
542#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
544pub enum Appearance {
545 Light,
546 Dark,
547}
548
549#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
551pub enum Orientation {
552 Horizontal,
553 Vertical,
554}
555
556#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
558pub struct Color {
559 pub r: f32,
560 pub g: f32,
561 pub b: f32,
562 pub a: f32,
563}
564
565impl Color {
566 pub const BLACK: Color = Color { r: 0.0, g: 0.0, b: 0.0, a: 1.0 };
567 pub const WHITE: Color = Color { r: 1.0, g: 1.0, b: 1.0, a: 1.0 };
568 pub const TRANSPARENT: Color = Color { r: 0.0, g: 0.0, b: 0.0, a: 0.0 };
569 pub const RED: Color = Color { r: 1.0, g: 0.0, b: 0.0, a: 1.0 };
570 pub const GREEN: Color = Color { r: 0.0, g: 1.0, b: 0.0, a: 1.0 };
571 pub const BLUE: Color = Color { r: 0.0, g: 0.0, b: 1.0, a: 1.0 };
572
573 pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
575 Self { r, g, b, a }
576 }
577
578 pub fn as_array(&self) -> [f32; 4] {
580 [self.r, self.g, self.b, self.a]
581 }
582}
583
584impl View for Color {
585 type Body = Never;
586 fn body(self) -> Self::Body { unreachable!() }
587
588 fn render(&self, renderer: &mut dyn Renderer, rect: Rect) {
589 renderer.fill_rect(rect, self.as_array());
590 }
591}
592
593pub struct AppearanceKey;
595
596impl EnvKey for AppearanceKey {
597 type Value = Appearance;
598 fn default_value() -> Self::Value {
599 Appearance::Dark }
601}
602
603pub struct StyleResolver;
605
606impl StyleResolver {
607 pub fn color(key: &str) -> String {
609 let tokens = Environment::<YggdrasilKey>::new().get();
610 let appearance = Environment::<AppearanceKey>::new().get();
611 let is_dark = appearance == Appearance::Dark;
612
613 tokens.get_color(key, is_dark).unwrap_or_else(|| "#FF00FF".to_string()) }
615
616 pub fn get<T: FromStr>(category: &str, key: &str) -> Option<T> {
618 let tokens = Environment::<YggdrasilKey>::new().get();
619 let appearance = Environment::<AppearanceKey>::new().get();
620 let is_dark = appearance == Appearance::Dark;
621
622 tokens.get(category, key, is_dark)
623 }
624}
625
626pub fn default_tokens() -> YggdrasilTokens {
628 let mut tokens = YggdrasilTokens::new();
629
630 tokens.color.insert("background".to_string(), TokenValue::Single {
632 value: "#000000".to_string(), });
634
635 tokens.color.insert("primary".to_string(), TokenValue::Single {
636 value: "#00FFFF".to_string(), });
638
639 tokens.color.insert("secondary".to_string(), TokenValue::Single {
640 value: "#FF00FF".to_string(), });
642
643 tokens.color.insert("surface".to_string(), TokenValue::Adaptive {
644 light: "#FFFFFF".to_string(),
645 dark: "#121212".to_string(),
646 });
647
648 tokens.color.insert("text".to_string(), TokenValue::Adaptive {
649 light: "#000000".to_string(),
650 dark: "#FFFFFF".to_string(),
651 });
652
653 tokens.bifrost.insert("blur".to_string(), TokenValue::Single {
655 value: "25.0".to_string(),
656 });
657 tokens.bifrost.insert("saturation".to_string(), TokenValue::Single {
658 value: "1.2".to_string(),
659 });
660 tokens.bifrost.insert("opacity".to_string(), TokenValue::Single {
661 value: "0.65".to_string(),
662 });
663
664 tokens.gungnir.insert("intensity".to_string(), TokenValue::Single {
666 value: "1.0".to_string(),
667 });
668 tokens.gungnir.insert("radius".to_string(), TokenValue::Single {
669 value: "15.0".to_string(),
670 });
671
672 tokens.mjolnir.insert("clip_angle".to_string(), TokenValue::Single {
674 value: "12.0".to_string(),
675 });
676 tokens.mjolnir.insert("border_width".to_string(), TokenValue::Single {
677 value: "2.0".to_string(),
678 });
679
680 tokens.anim.insert("stiffness".to_string(), TokenValue::Single {
682 value: "170.0".to_string(),
683 });
684 tokens.anim.insert("damping".to_string(), TokenValue::Single {
685 value: "26.0".to_string(),
686 });
687 tokens.anim.insert("mass".to_string(), TokenValue::Single {
688 value: "1.0".to_string(),
689 });
690
691 tokens.accessibility.insert("reduce_motion".to_string(), TokenValue::Single {
693 value: "false".to_string(),
694 });
695
696 tokens
697}
698
699
700
701
702pub struct Environment<K: EnvKey> {
704 _marker: std::marker::PhantomData<K>,
705}
706
707impl<K: EnvKey> Environment<K> {
708 pub fn new() -> Self {
710 Self {
711 _marker: std::marker::PhantomData,
712 }
713 }
714
715 pub fn get(&self) -> K::Value {
717 if let Some(env_store) = ENVIRONMENT.get() {
718 let env_lock = env_store.lock().unwrap();
719 if let Some(val) = env_lock.get(&std::any::TypeId::of::<K>()) {
720 if let Some(typed_val) = val.downcast_ref::<K::Value>() {
721 return typed_val.clone();
722 }
723 }
724 }
725 K::default_value()
726 }
727}
728mod env {
729
730
731
732
733 pub fn insert<K: super::EnvKey>(value: K::Value) {
735 if let Some(store) = super::ENVIRONMENT.get() {
736 let mut env_map = store.lock().unwrap();
737 env_map.insert(std::any::TypeId::of::<K>(), Box::new(value));
738 }
739 }
740
741 pub fn remove<K: super::EnvKey>() {
743 if let Some(store) = super::ENVIRONMENT.get() {
744 let mut env_map = store.lock().unwrap();
745 env_map.remove(&std::any::TypeId::of::<K>());
746 }
747 }
748}
749
750
751
752#[derive(Debug, Clone, Copy, PartialEq)]
756pub struct Size {
757 pub width: f32,
758 pub height: f32,
759}
760
761#[derive(Debug, Clone, Copy, PartialEq)]
763pub struct EdgeInsets {
764 pub top: f32,
765 pub leading: f32,
766 pub bottom: f32,
767 pub trailing: f32,
768}
769
770impl EdgeInsets {
771 pub fn all(value: f32) -> Self {
773 Self {
774 top: value,
775 leading: value,
776 bottom: value,
777 trailing: value,
778 }
779 }
780
781 pub fn vertical(value: f32) -> Self {
783 Self {
784 top: value,
785 leading: 0.0,
786 bottom: value,
787 trailing: 0.0,
788 }
789 }
790
791 pub fn horizontal(value: f32) -> Self {
793 Self {
794 top: 0.0,
795 leading: value,
796 bottom: 0.0,
797 trailing: value,
798 }
799 }
800}
801
802#[derive(Debug, Clone, Copy, PartialEq)]
804pub struct FrameModifier {
805 pub width: Option<f32>,
806 pub height: Option<f32>,
807}
808
809impl FrameModifier {
810 pub fn new() -> Self {
811 Self {
812 width: None,
813 height: None,
814 }
815 }
816
817 pub fn width(mut self, width: f32) -> Self {
818 self.width = Some(width);
819 self
820 }
821
822 pub fn height(mut self, height: f32) -> Self {
823 self.height = Some(height);
824 self
825 }
826
827 pub fn size(mut self, width: f32, height: f32) -> Self {
828 self.width = Some(width);
829 self.height = Some(height);
830 self
831 }
832}
833
834impl ViewModifier for FrameModifier {
835 fn modify<V: View>(self, content: V) -> impl View {
836 ModifiedView::new(content, self)
837 }
838}
839
840
841#[derive(Debug, Clone, Copy, PartialEq)]
843pub struct OffsetModifier {
844 pub x: f32,
845 pub y: f32,
846}
847
848impl OffsetModifier {
849 pub fn new(x: f32, y: f32) -> Self {
850 Self { x, y }
851 }
852}
853
854impl ViewModifier for OffsetModifier {
855 fn modify<V: View>(self, content: V) -> impl View {
856 ModifiedView::new(content, self)
857 }
858}
859
860#[derive(Debug, Clone, Copy, PartialEq, Eq)]
862pub struct ZIndexModifier {
863 pub z_index: i32,
864}
865
866impl ZIndexModifier {
867 pub fn new(z_index: i32) -> Self {
868 Self { z_index }
869 }
870}
871
872impl ViewModifier for ZIndexModifier {
873 fn modify<V: View>(self, content: V) -> impl View {
874 ModifiedView::new(content, self)
875 }
876}
877
878#[derive(Debug, Clone, Copy, PartialEq)]
880pub struct LayoutConstraints {
881 pub min_width: Option<f32>,
882 pub max_width: Option<f32>,
883 pub min_height: Option<f32>,
884 pub max_height: Option<f32>,
885}
886
887impl Default for LayoutConstraints {
888 fn default() -> Self {
889 Self {
890 min_width: None,
891 max_width: None,
892 min_height: None,
893 max_height: None,
894 }
895 }
896}
897
898#[derive(Debug, Clone, Copy, PartialEq)]
900pub struct LayoutModifier {
901 pub constraints: LayoutConstraints,
902}
903
904impl LayoutModifier {
905 pub fn new(constraints: LayoutConstraints) -> Self {
906 Self { constraints }
907 }
908}
909
910impl ViewModifier for LayoutModifier {
911 fn modify<V: View>(self, content: V) -> impl View {
912 ModifiedView::new(content, self)
913 }
914}
915
916#[derive(Debug, Clone, Copy, PartialEq)]
918pub struct FlexModifier {
919 pub flex: f32,
920}
921
922impl FlexModifier {
923 pub fn new(flex: f32) -> Self {
924 Self { flex }
925 }
926}
927
928impl ViewModifier for FlexModifier {
929 fn modify<V: View>(self, content: V) -> impl View {
930 ModifiedView::new(content, self)
931 }
932}
933
934pub mod layout {
936 use super::*;
937
938 pub struct LayoutCache;
940
941 impl LayoutCache {
942 pub fn new() -> Self {
943 Self
944 }
945
946 pub fn clear(&mut self) {
947 }
949 }
950
951 #[derive(Debug, Clone, Copy, PartialEq)]
953 pub struct SizeProposal {
954 pub width: Option<f32>,
955 pub height: Option<f32>,
956 }
957
958 impl SizeProposal {
959 pub fn unspecified() -> Self {
960 Self {
961 width: None,
962 height: None,
963 }
964 }
965
966 pub fn width(width: f32) -> Self {
967 Self {
968 width: Some(width),
969 height: None,
970 }
971 }
972
973 pub fn height(height: f32) -> Self {
974 Self {
975 width: None,
976 height: Some(height),
977 }
978 }
979
980 pub fn tight(width: f32, height: f32) -> Self {
981 Self {
982 width: Some(width),
983 height: Some(height),
984 }
985 }
986 }
987
988 pub trait LayoutView: Send {
990 fn size_that_fits(&self, proposal: SizeProposal, subviews: &[&dyn LayoutView], cache: &mut LayoutCache) -> Size;
992
993 fn place_subviews(&self, bounds: Rect, subviews: &mut [&mut dyn LayoutView], cache: &mut LayoutCache);
995 }
996
997 #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
999 pub struct Rect {
1000 pub x: f32,
1001 pub y: f32,
1002 pub width: f32,
1003 pub height: f32,
1004 }
1005
1006 impl Rect {
1007 pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
1008 Self { x, y, width, height }
1009 }
1010
1011 pub fn zero() -> Self {
1012 Self { x: 0.0, y: 0.0, width: 0.0, height: 0.0 }
1013 }
1014
1015 pub fn size(&self) -> Size {
1016 Size { width: self.width, height: self.height }
1017 }
1018
1019 pub fn split_horizontal(&self, n: usize) -> Vec<Rect> {
1021 if n == 0 { return vec![]; }
1022 let item_width = self.width / n as f32;
1023 (0..n).map(|i| Rect {
1024 x: self.x + i as f32 * item_width,
1025 y: self.y,
1026 width: item_width,
1027 height: self.height,
1028 }).collect()
1029 }
1030
1031 pub fn split_vertical(&self, n: usize) -> Vec<Rect> {
1033 if n == 0 { return vec![]; }
1034 let item_height = self.height / n as f32;
1035 (0..n).map(|i| Rect {
1036 x: self.x,
1037 y: self.y + i as f32 * item_height,
1038 width: self.width,
1039 height: item_height,
1040 }).collect()
1041 }
1042 }
1043}
1044
1045pub use layout::{LayoutView, SizeProposal, Rect, LayoutCache};
1047
1048pub mod runtime;
1049pub mod scene_graph;
1050
1051pub use scene_graph::{NodeId, bifrost_registry};
1052
1053#[cfg(test)]
1054mod phase1_test;