use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::str::FromStr;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum TokenValue {
Single {
value: String,
},
Adaptive {
light: String,
dark: String,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct YggdrasilTokens {
pub color: HashMap<String, TokenValue>,
pub font: HashMap<String, TokenValue>,
pub spacing: HashMap<String, TokenValue>,
pub radius: HashMap<String, TokenValue>,
pub shadow: HashMap<String, TokenValue>,
pub border: HashMap<String, TokenValue>,
pub anim: HashMap<String, TokenValue>,
pub bifrost: HashMap<String, TokenValue>,
pub gungnir: HashMap<String, TokenValue>,
pub mjolnir: HashMap<String, TokenValue>,
pub accessibility: HashMap<String, TokenValue>,
}
impl YggdrasilTokens {
pub fn new() -> Self {
Self {
color: HashMap::new(),
font: HashMap::new(),
spacing: HashMap::new(),
radius: HashMap::new(),
shadow: HashMap::new(),
border: HashMap::new(),
anim: HashMap::new(),
bifrost: HashMap::new(),
gungnir: HashMap::new(),
mjolnir: HashMap::new(),
accessibility: HashMap::new(),
}
}
pub fn get_color(&self, key: &str, is_dark: bool) -> Option<String> {
self.color.get(key).and_then(|token| {
match token {
TokenValue::Single { value } => Some(value.clone()),
TokenValue::Adaptive { light, dark } => {
if is_dark { Some(dark.clone()) } else { Some(light.clone()) }
}
}
})
}
pub fn get<T: FromStr>(&self, category: &str, key: &str, is_dark: bool) -> Option<T> {
let map = match category {
"color" => &self.color,
"font" => &self.font,
"spacing" => &self.spacing,
"radius" => &self.radius,
"shadow" => &self.shadow,
"border" => &self.border,
"anim" => &self.anim,
"bifrost" => &self.bifrost,
"gungnir" => &self.gungnir,
"mjolnir" => &self.mjolnir,
"accessibility" => &self.accessibility,
_ => return None,
};
map.get(key).and_then(|token| {
match token {
TokenValue::Single { value } => value.parse().ok(),
TokenValue::Adaptive { light, dark } => {
let value = if is_dark { dark } else { light };
value.parse().ok()
}
}
})
}
}
pub trait View: Sized + Send {
type Body: View;
fn body(self) -> Self::Body;
fn render(&self, _renderer: &mut dyn Renderer, _rect: Rect) {}
fn modifier<M: ViewModifier>(self, m: M) -> ModifiedView<Self, M> {
ModifiedView::new(self, m)
}
fn bifrost(self, blur: f32, saturation: f32, opacity: f32) -> ModifiedView<Self, BifrostModifier> {
self.modifier(BifrostModifier { blur, saturation, opacity })
}
fn gungnir(self, color: impl Into<String>, radius: f32, intensity: f32) -> ModifiedView<Self, GungnirModifier> {
self.modifier(GungnirModifier { color: color.into(), radius, intensity })
}
fn mjolnir_slice(self, angle: f32, offset: f32) -> ModifiedView<Self, MjolnirSliceModifier> {
self.modifier(MjolnirSliceModifier { angle, offset })
}
fn mjolnir_shatter(self, pieces: u32, force: f32) -> ModifiedView<Self, MjolnirShatterModifier> {
self.modifier(MjolnirShatterModifier { pieces, force })
}
fn bifrost_bridge(self, id: impl Into<String>) -> ModifiedView<Self, BifrostBridgeModifier> {
self.modifier(BifrostBridgeModifier { id: id.into() })
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct BifrostBridgeModifier {
pub id: String,
}
impl ViewModifier for BifrostBridgeModifier {
fn modify<V: View>(self, content: V) -> impl View {
ModifiedView::new(content, self)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct MjolnirSliceModifier {
pub angle: f32,
pub offset: f32,
}
impl ViewModifier for MjolnirSliceModifier {
fn modify<V: View>(self, content: V) -> impl View {
ModifiedView::new(content, self)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct MjolnirShatterModifier {
pub pieces: u32,
pub force: f32,
}
impl ViewModifier for MjolnirShatterModifier {
fn modify<V: View>(self, content: V) -> impl View {
ModifiedView::new(content, self)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct BifrostModifier {
pub blur: f32,
pub saturation: f32,
pub opacity: f32,
}
impl ViewModifier for BifrostModifier {
fn modify<V: View>(self, content: V) -> impl View {
ModifiedView::new(content, self)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct GungnirModifier {
pub color: String,
pub radius: f32,
pub intensity: f32,
}
impl ViewModifier for GungnirModifier {
fn modify<V: View>(self, content: V) -> impl View {
ModifiedView::new(content, self)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SleipnirModifier<T> {
pub target: T,
pub stiffness: f32,
pub damping: f32,
}
impl<T: Send + Sync + 'static> ViewModifier for SleipnirModifier<T> {
fn modify<V: View>(self, content: V) -> impl View {
ModifiedView::new(content, self)
}
}
#[doc(hidden)]
pub enum Never {}
impl View for Never {
type Body = Never;
fn body(self) -> Never {
unreachable!()
}
}
pub struct ModifiedView<V, M> {
view: V,
modifier: M,
}
impl<V: View, M: ViewModifier> ModifiedView<V, M> {
#[doc(hidden)]
pub fn new(view: V, modifier: M) -> Self {
Self { view, modifier }
}
}
impl<V: View, M: ViewModifier> View for ModifiedView<V, M> {
type Body = ModifiedView<V::Body, M>;
fn body(self) -> Self::Body {
ModifiedView {
view: self.view.body(),
modifier: self.modifier,
}
}
}
pub trait ViewModifier: Send {
fn modify<V: View>(self, content: V) -> impl View;
}
pub trait Renderer {
fn begin_frame(&mut self);
fn end_frame(&mut self);
fn fill_rect(&mut self, rect: Rect, color: [f32; 4]);
fn stroke_rect(&mut self, rect: Rect, color: [f32; 4], stroke_width: f32);
fn fill_rounded_rect(&mut self, rect: Rect, radius: f32, color: [f32; 4]);
fn draw_text(&mut self, text: &str, x: f32, y: f32, size: f32, color: [f32; 4]);
}
use std::sync::Arc;
#[derive(Clone)]
pub struct State<T: Clone + Send + Sync + 'static> {
value: Arc<std::sync::RwLock<T>>,
subscribers: Arc<std::sync::RwLock<Vec<Box<dyn FnMut(&T) + Send + Sync>>>>,
}
impl<T: Clone + Send + Sync + 'static> State<T> {
pub fn new(value: T) -> Self {
Self {
value: Arc::new(std::sync::RwLock::new(value)),
subscribers: Arc::new(std::sync::RwLock::new(Vec::new())),
}
}
pub fn get(&self) -> T {
self.value.read().unwrap().clone()
}
pub fn set(&self, value: T) {
*self.value.write().unwrap() = value;
let mut subscribers = self.subscribers.write().unwrap();
for subscriber in subscribers.iter_mut() {
subscriber(&self.get());
}
}
pub fn subscribe<F: FnMut(&T) + Send + Sync + 'static>(&self, callback: F) {
self.subscribers.write().unwrap().push(Box::new(callback));
}
}
#[derive(Clone)]
pub struct Binding<T: Clone + Send + Sync + 'static> {
state: Arc<std::sync::RwLock<T>>,
}
impl<T: Clone + Send + Sync + 'static> Binding<T> {
pub fn from_state(state: &State<T>) -> Self {
Self {
state: state.value.clone(),
}
}
pub fn get(&self) -> T {
self.state.read().unwrap().clone()
}
pub fn set(&self, value: T) {
*self.state.write().unwrap() = value;
}
}
use std::any::TypeId;
use std::sync::{Mutex, OnceLock};
pub(crate) static ENVIRONMENT: OnceLock<Mutex<HashMap<TypeId, Box<dyn std::any::Any + Send + Sync>>>> = OnceLock::new();
pub trait EnvKey: 'static + Send + Sync {
type Value: Clone + Send + Sync + 'static;
fn default_value() -> Self::Value;
}
pub struct YggdrasilKey;
impl EnvKey for YggdrasilKey {
type Value = YggdrasilTokens;
fn default_value() -> Self::Value {
default_tokens()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Appearance {
Light,
Dark,
}
pub struct AppearanceKey;
impl EnvKey for AppearanceKey {
type Value = Appearance;
fn default_value() -> Self::Value {
Appearance::Dark }
}
pub struct StyleResolver;
impl StyleResolver {
pub fn color(key: &str) -> String {
let tokens = Environment::<YggdrasilKey>::new().get();
let appearance = Environment::<AppearanceKey>::new().get();
let is_dark = appearance == Appearance::Dark;
tokens.get_color(key, is_dark).unwrap_or_else(|| "#FF00FF".to_string()) }
pub fn get<T: FromStr>(category: &str, key: &str) -> Option<T> {
let tokens = Environment::<YggdrasilKey>::new().get();
let appearance = Environment::<AppearanceKey>::new().get();
let is_dark = appearance == Appearance::Dark;
tokens.get(category, key, is_dark)
}
}
pub fn default_tokens() -> YggdrasilTokens {
let mut tokens = YggdrasilTokens::new();
tokens.color.insert("background".to_string(), TokenValue::Single {
value: "#000000".to_string(), });
tokens.color.insert("primary".to_string(), TokenValue::Single {
value: "#00FFFF".to_string(), });
tokens.color.insert("secondary".to_string(), TokenValue::Single {
value: "#FF00FF".to_string(), });
tokens.color.insert("surface".to_string(), TokenValue::Adaptive {
light: "#FFFFFF".to_string(),
dark: "#121212".to_string(),
});
tokens.color.insert("text".to_string(), TokenValue::Adaptive {
light: "#000000".to_string(),
dark: "#FFFFFF".to_string(),
});
tokens.bifrost.insert("blur".to_string(), TokenValue::Single {
value: "25.0".to_string(),
});
tokens.bifrost.insert("saturation".to_string(), TokenValue::Single {
value: "1.2".to_string(),
});
tokens.bifrost.insert("opacity".to_string(), TokenValue::Single {
value: "0.65".to_string(),
});
tokens.gungnir.insert("intensity".to_string(), TokenValue::Single {
value: "1.0".to_string(),
});
tokens.gungnir.insert("radius".to_string(), TokenValue::Single {
value: "15.0".to_string(),
});
tokens.mjolnir.insert("clip_angle".to_string(), TokenValue::Single {
value: "12.0".to_string(),
});
tokens.mjolnir.insert("border_width".to_string(), TokenValue::Single {
value: "2.0".to_string(),
});
tokens.anim.insert("stiffness".to_string(), TokenValue::Single {
value: "170.0".to_string(),
});
tokens.anim.insert("damping".to_string(), TokenValue::Single {
value: "26.0".to_string(),
});
tokens.anim.insert("mass".to_string(), TokenValue::Single {
value: "1.0".to_string(),
});
tokens.accessibility.insert("reduce_motion".to_string(), TokenValue::Single {
value: "false".to_string(),
});
tokens
}
pub struct Environment<K: EnvKey> {
_marker: std::marker::PhantomData<K>,
}
impl<K: EnvKey> Environment<K> {
pub fn new() -> Self {
Self {
_marker: std::marker::PhantomData,
}
}
pub fn get(&self) -> K::Value {
if let Some(env_store) = ENVIRONMENT.get() {
let env_lock = env_store.lock().unwrap();
if let Some(val) = env_lock.get(&std::any::TypeId::of::<K>()) {
if let Some(typed_val) = val.downcast_ref::<K::Value>() {
return typed_val.clone();
}
}
}
K::default_value()
}
}
mod env {
pub fn insert<K: super::EnvKey>(value: K::Value) {
if let Some(store) = super::ENVIRONMENT.get() {
let mut env_map = store.lock().unwrap();
env_map.insert(std::any::TypeId::of::<K>(), Box::new(value));
}
}
pub fn remove<K: super::EnvKey>() {
if let Some(store) = super::ENVIRONMENT.get() {
let mut env_map = store.lock().unwrap();
env_map.remove(&std::any::TypeId::of::<K>());
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Size {
pub width: f32,
pub height: f32,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct EdgeInsets {
pub top: f32,
pub leading: f32,
pub bottom: f32,
pub trailing: f32,
}
impl EdgeInsets {
pub fn all(value: f32) -> Self {
Self {
top: value,
leading: value,
bottom: value,
trailing: value,
}
}
pub fn vertical(value: f32) -> Self {
Self {
top: value,
leading: 0.0,
bottom: value,
trailing: 0.0,
}
}
pub fn horizontal(value: f32) -> Self {
Self {
top: 0.0,
leading: value,
bottom: 0.0,
trailing: value,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct FrameModifier {
pub width: Option<f32>,
pub height: Option<f32>,
}
impl FrameModifier {
pub fn new() -> Self {
Self {
width: None,
height: None,
}
}
pub fn width(mut self, width: f32) -> Self {
self.width = Some(width);
self
}
pub fn height(mut self, height: f32) -> Self {
self.height = Some(height);
self
}
pub fn size(mut self, width: f32, height: f32) -> Self {
self.width = Some(width);
self.height = Some(height);
self
}
}
impl ViewModifier for FrameModifier {
fn modify<V: View>(self, content: V) -> impl View {
ModifiedView::new(content, self)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct PaddingModifier {
pub insets: EdgeInsets,
}
impl PaddingModifier {
pub fn new(insets: EdgeInsets) -> Self {
Self { insets }
}
pub fn all(value: f32) -> Self {
Self::new(EdgeInsets::all(value))
}
pub fn vertical(value: f32) -> Self {
Self::new(EdgeInsets::vertical(value))
}
pub fn horizontal(value: f32) -> Self {
Self::new(EdgeInsets::horizontal(value))
}
}
impl ViewModifier for PaddingModifier {
fn modify<V: View>(self, content: V) -> impl View {
ModifiedView::new(content, self)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct OffsetModifier {
pub x: f32,
pub y: f32,
}
impl OffsetModifier {
pub fn new(x: f32, y: f32) -> Self {
Self { x, y }
}
}
impl ViewModifier for OffsetModifier {
fn modify<V: View>(self, content: V) -> impl View {
ModifiedView::new(content, self)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ZIndexModifier {
pub z_index: i32,
}
impl ZIndexModifier {
pub fn new(z_index: i32) -> Self {
Self { z_index }
}
}
impl ViewModifier for ZIndexModifier {
fn modify<V: View>(self, content: V) -> impl View {
ModifiedView::new(content, self)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct LayoutConstraints {
pub min_width: Option<f32>,
pub max_width: Option<f32>,
pub min_height: Option<f32>,
pub max_height: Option<f32>,
}
impl Default for LayoutConstraints {
fn default() -> Self {
Self {
min_width: None,
max_width: None,
min_height: None,
max_height: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct LayoutModifier {
pub constraints: LayoutConstraints,
}
impl LayoutModifier {
pub fn new(constraints: LayoutConstraints) -> Self {
Self { constraints }
}
}
impl ViewModifier for LayoutModifier {
fn modify<V: View>(self, content: V) -> impl View {
ModifiedView::new(content, self)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct FlexModifier {
pub flex: f32,
}
impl FlexModifier {
pub fn new(flex: f32) -> Self {
Self { flex }
}
}
impl ViewModifier for FlexModifier {
fn modify<V: View>(self, content: V) -> impl View {
ModifiedView::new(content, self)
}
}
pub mod layout {
use super::*;
pub struct LayoutCache;
impl LayoutCache {
pub fn new() -> Self {
Self
}
pub fn clear(&mut self) {
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct SizeProposal {
pub width: Option<f32>,
pub height: Option<f32>,
}
impl SizeProposal {
pub fn unspecified() -> Self {
Self {
width: None,
height: None,
}
}
pub fn width(width: f32) -> Self {
Self {
width: Some(width),
height: None,
}
}
pub fn height(height: f32) -> Self {
Self {
width: None,
height: Some(height),
}
}
pub fn tight(width: f32, height: f32) -> Self {
Self {
width: Some(width),
height: Some(height),
}
}
}
pub trait LayoutView: Send {
fn size_that_fits(&self, proposal: SizeProposal, subviews: &[&dyn LayoutView], cache: &mut LayoutCache) -> Size;
fn place_subviews(&self, bounds: Rect, subviews: &mut [&mut dyn LayoutView], cache: &mut LayoutCache);
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Rect {
pub x: f32,
pub y: f32,
pub width: f32,
pub height: f32,
}
impl Rect {
pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
Self { x, y, width, height }
}
pub fn zero() -> Self {
Self { x: 0.0, y: 0.0, width: 0.0, height: 0.0 }
}
pub fn size(&self) -> Size {
Size { width: self.width, height: self.height }
}
}
}
pub use layout::{LayoutView, SizeProposal, Rect, LayoutCache};
pub mod runtime;
pub mod scene_graph;
pub use scene_graph::{NodeId, bifrost_registry};
#[cfg(test)]
mod phase1_test;