1use std::any::{Any, type_name};
12use std::fmt::{self, Debug};
13use std::hash::{BuildHasherDefault, Hash, Hasher};
14use std::ptr;
15use std::rc::Rc;
16
17use imbl::shared_ptr::DefaultSharedPtr;
18use rustc_hash::FxHasher;
19
20#[cfg(not(target_arch = "wasm32"))]
21use std::time::Instant;
22#[cfg(target_arch = "wasm32")]
23use web_time::Instant;
24
25use crate::view::{IntoView, View};
26use crate::views::Label;
27
28use super::Style;
29use super::selectors::StyleSelectors;
30use super::transition::TransitionState;
31use super::values::{CombineResult, StyleMapValue, StylePropValue, StyleValue};
32
33pub trait StyleClass: Default + Copy + 'static {
38 fn key() -> StyleKey;
39 fn class_ref() -> StyleClassRef {
40 StyleClassRef { key: Self::key() }
41 }
42}
43
44#[derive(Debug, Clone)]
45pub struct StyleClassInfo {
46 pub(crate) name: fn() -> &'static str,
47}
48
49impl StyleClassInfo {
50 pub const fn new<Name>() -> Self {
51 StyleClassInfo {
52 name: || std::any::type_name::<Name>(),
53 }
54 }
55}
56
57#[derive(Copy, Clone, Debug, PartialEq)]
58pub struct StyleClassRef {
59 pub key: StyleKey,
60}
61
62macro_rules! style_key_selector {
63 ($v:vis $name:ident, $sel:expr) => {
64 fn $name() -> $crate::style::StyleKey {
65 static INFO: $crate::style::StyleKeyInfo = $crate::style::StyleKeyInfo::Selector($sel);
66 $crate::style::StyleKey { info: &INFO }
67 }
68 };
69}
70
71pub(crate) use style_key_selector;
72
73#[macro_export]
74macro_rules! style_class {
75 ($(#[$meta:meta])* $v:vis $name:ident) => {
76 $(#[$meta])*
77 #[derive(Default, Copy, Clone)]
78 $v struct $name;
79
80 impl $crate::style::StyleClass for $name {
81 fn key() -> $crate::style::StyleKey {
82 static INFO: $crate::style::StyleKeyInfo = $crate::style::StyleKeyInfo::Class(
83 $crate::style::StyleClassInfo::new::<$name>()
84 );
85 $crate::style::StyleKey { info: &INFO }
86 }
87 }
88 };
89}
90
91pub trait StyleProp: Default + Copy + 'static {
96 type Type: StylePropValue;
97 fn key() -> StyleKey;
98 fn prop_ref() -> StylePropRef {
99 StylePropRef { key: Self::key() }
100 }
101 fn default_value() -> Self::Type;
102}
103
104pub(crate) type InterpolateFn =
105 fn(val1: &dyn Any, val2: &dyn Any, time: f64) -> Option<Rc<dyn Any>>;
106
107pub(crate) type CombineFn = fn(val1: Rc<dyn Any>, val2: Rc<dyn Any>) -> Rc<dyn Any>;
108
109pub(crate) type HashAnyFn = fn(val: &dyn Any) -> u64;
111
112pub(crate) type EqAnyFn = fn(val1: &dyn Any, val2: &dyn Any) -> bool;
114
115#[derive(Debug)]
116pub struct StylePropInfo {
117 pub(crate) name: fn() -> &'static str,
118 pub(crate) inherited: bool,
119 #[allow(unused)]
120 pub(crate) default_as_any: fn() -> Rc<dyn Any>,
121 pub(crate) interpolate: InterpolateFn,
122 pub(crate) debug_any: fn(val: &dyn Any) -> String,
123 pub(crate) debug_view: fn(val: &dyn Any) -> Option<Box<dyn View>>,
124 pub(crate) combine: CombineFn,
125 pub(crate) transition_key: StyleKey,
126 pub(crate) hash_any: HashAnyFn,
128 pub(crate) eq_any: EqAnyFn,
130}
131
132impl StylePropInfo {
133 pub const fn new<Name, T: StylePropValue + 'static>(
134 inherited: bool,
135 default_as_any: fn() -> Rc<dyn Any>,
136 transition_key: StyleKey,
137 ) -> Self {
138 StylePropInfo {
139 name: || std::any::type_name::<Name>(),
140 inherited,
141 default_as_any,
142 debug_any: |val| {
143 if let Some(v) = val.downcast_ref::<StyleMapValue<T>>() {
144 match v {
145 StyleMapValue::Val(v) | StyleMapValue::Animated(v) => format!("{v:?}"),
146
147 StyleMapValue::Unset => "Unset".to_owned(),
148 }
149 } else {
150 panic!(
151 "expected type {} for property {}",
152 type_name::<T>(),
153 std::any::type_name::<Name>(),
154 )
155 }
156 },
157 interpolate: |val1, val2, time| {
158 if let (Some(v1), Some(v2)) = (
159 val1.downcast_ref::<StyleMapValue<T>>(),
160 val2.downcast_ref::<StyleMapValue<T>>(),
161 ) {
162 if let (
163 StyleMapValue::Val(v1) | StyleMapValue::Animated(v1),
164 StyleMapValue::Val(v2) | StyleMapValue::Animated(v2),
165 ) = (v1, v2)
166 {
167 v1.interpolate(v2, time)
168 .map(|val| Rc::new(StyleMapValue::Animated(val)) as Rc<dyn Any>)
169 } else {
170 None
171 }
172 } else {
173 panic!(
174 "expected type {} for property {}. Got typeids {:?} and {:?}",
175 type_name::<T>(),
176 std::any::type_name::<Name>(),
177 val1.type_id(),
178 val2.type_id()
179 )
180 }
181 },
182 debug_view: |val| {
183 if let Some(v) = val.downcast_ref::<StyleMapValue<T>>() {
184 match v {
185 StyleMapValue::Val(v) | StyleMapValue::Animated(v) => v.debug_view(),
186
187 StyleMapValue::Unset => Some(Label::new("Unset").into_any()),
188 }
189 } else {
190 panic!(
191 "expected type {} for property {}",
192 type_name::<T>(),
193 std::any::type_name::<Name>(),
194 )
195 }
196 },
197 combine: |val1, val2| {
198 if let (Some(v1), Some(v2)) = (
199 val1.downcast_ref::<StyleMapValue<T>>(),
200 val2.downcast_ref::<StyleMapValue<T>>(),
201 ) {
202 match (v1, v2) {
203 (StyleMapValue::Val(a), StyleMapValue::Val(b)) => match a.combine(b) {
204 CombineResult::Other => val2,
205 CombineResult::New(result) => {
206 Rc::new(StyleMapValue::Val(result)) as Rc<dyn Any>
207 }
208 },
209 (StyleMapValue::Unset, _) => val2,
210 (_, StyleMapValue::Unset) => val2,
211 (
212 StyleMapValue::Val(a) | StyleMapValue::Animated(a),
213 StyleMapValue::Animated(b) | StyleMapValue::Val(b),
214 ) => match a.combine(b) {
215 CombineResult::Other => val2,
216 CombineResult::New(result) => {
217 Rc::new(StyleMapValue::Animated(result)) as Rc<dyn Any>
218 }
219 },
220 }
221 } else {
222 panic!(
223 "expected type {} for property {}. Got typeids {:?} and {:?}",
224 type_name::<StyleMapValue<T>>(),
225 std::any::type_name::<Name>(),
226 val1.type_id(),
227 val2.type_id()
228 )
229 }
230 },
231 transition_key,
232 hash_any: |val| {
233 if let Some(v) = val.downcast_ref::<StyleMapValue<T>>() {
234 match v {
235 StyleMapValue::Val(v) | StyleMapValue::Animated(v) => v.content_hash(),
236 StyleMapValue::Unset => 0, }
238 } else {
239 panic!(
240 "expected type {} for property {}",
241 type_name::<T>(),
242 std::any::type_name::<Name>(),
243 )
244 }
245 },
246 eq_any: |val1, val2| {
247 if let (Some(v1), Some(v2)) = (
248 val1.downcast_ref::<StyleMapValue<T>>(),
249 val2.downcast_ref::<StyleMapValue<T>>(),
250 ) {
251 match (v1, v2) {
252 (
253 StyleMapValue::Val(a) | StyleMapValue::Animated(a),
254 StyleMapValue::Val(b) | StyleMapValue::Animated(b),
255 ) => {
256 a.content_hash() == b.content_hash()
258 }
259 (StyleMapValue::Unset, StyleMapValue::Unset) => true,
260 _ => false,
261 }
262 } else {
263 panic!(
264 "expected type {} for property {}. Got typeids {:?} and {:?}",
265 type_name::<T>(),
266 std::any::type_name::<Name>(),
267 val1.type_id(),
268 val2.type_id()
269 )
270 }
271 },
272 }
273 }
274}
275
276#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
277pub struct StylePropRef {
278 pub key: StyleKey,
279}
280
281impl StylePropRef {
282 pub(crate) fn info(&self) -> &StylePropInfo {
283 if let StyleKeyInfo::Prop(prop) = self.key.info {
284 prop
285 } else {
286 panic!()
287 }
288 }
289}
290
291pub trait StylePropReader {
296 type State: Debug;
297 type Type: Clone;
298
299 fn read(
302 state: &mut Self::State,
303 style: &Style,
304 fallback: &Style,
305 now: &Instant,
306 request_transition: &mut bool,
307 ) -> bool;
308
309 fn get(state: &Self::State) -> Self::Type;
310 fn new() -> Self::State;
311}
312
313impl<P: StyleProp> StylePropReader for P {
314 type State = (P::Type, TransitionState<P::Type>);
315 type Type = P::Type;
316
317 fn read(
319 state: &mut Self::State,
320 style: &Style,
321 fallback: &Style,
322 now: &Instant,
323 request_transition: &mut bool,
324 ) -> bool {
325 let style_value = style.get_prop_style_value::<P>();
327 let mut prop_animated = false;
328 let new = match style_value {
329 StyleValue::Animated(val) => {
330 *request_transition = true;
331 prop_animated = true;
332 val
333 }
334 StyleValue::Val(val) => val,
335 StyleValue::Unset | StyleValue::Base => fallback
336 .get_prop::<P>()
337 .unwrap_or_else(|| P::default_value()),
338 };
339 state.1.read(
341 style
342 .get_transition::<P>()
343 .or_else(|| fallback.get_transition::<P>()),
344 );
345
346 let changed = new != state.0;
348 if changed && !prop_animated {
349 state.1.transition(&Self::get(state), &new);
350 state.0 = new;
351 } else if prop_animated {
352 state.0 = new;
353 }
354 changed | state.1.step(now, request_transition)
355 }
356
357 fn get(state: &Self::State) -> Self::Type {
359 state.1.get(&state.0)
360 }
361
362 fn new() -> Self::State {
363 (P::default_value(), TransitionState::default())
364 }
365}
366
367impl<P: StyleProp> StylePropReader for Option<P> {
368 type State = Option<P::Type>;
369 type Type = Option<P::Type>;
370 fn read(
371 state: &mut Self::State,
372 style: &Style,
373 fallback: &Style,
374 _now: &Instant,
375 _transition: &mut bool,
376 ) -> bool {
377 let new = style.get_prop::<P>().or_else(|| fallback.get_prop::<P>());
378 let changed = new != *state;
379 *state = new;
380 changed
381 }
382 fn get(state: &Self::State) -> Self::Type {
383 state.clone()
384 }
385 fn new() -> Self::State {
386 None
387 }
388}
389
390#[derive(Clone)]
395pub struct ExtractorField<R: StylePropReader> {
396 state: R::State,
397}
398
399impl<R: StylePropReader> Debug for ExtractorField<R> {
400 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
401 self.state.fmt(f)
402 }
403}
404
405impl<R: StylePropReader> ExtractorField<R> {
406 pub fn read(
407 &mut self,
408 style: &Style,
409 fallback: &Style,
410 now: &Instant,
411 request_transition: &mut bool,
412 ) -> bool {
413 R::read(&mut self.state, style, fallback, now, request_transition)
414 }
415 pub fn get(&self) -> R::Type {
416 R::get(&self.state)
417 }
418 #[allow(clippy::new_without_default)]
419 pub fn new() -> Self {
420 Self { state: R::new() }
421 }
422}
423
424impl<R: StylePropReader> PartialEq for ExtractorField<R>
425where
426 R::Type: PartialEq,
427{
428 fn eq(&self, other: &Self) -> bool {
429 self.get() == other.get()
430 }
431}
432
433impl<R: StylePropReader> Eq for ExtractorField<R> where R::Type: Eq {}
434
435impl<R: StylePropReader> std::hash::Hash for ExtractorField<R>
436where
437 R::Type: std::hash::Hash,
438{
439 fn hash<H: Hasher>(&self, state: &mut H) {
440 self.get().hash(state)
441 }
442}
443
444#[macro_export]
449macro_rules! prop {
450 ($(#[$meta:meta])* $v:vis $name:ident: $ty:ty { $($options:tt)* } = $default:expr
451 ) => {
452 $(#[$meta])*
453 #[derive(Default, Copy, Clone)]
454 #[allow(missing_docs)]
455 $v struct $name;
456 impl $crate::style::StyleProp for $name {
457 type Type = $ty;
458 fn key() -> $crate::style::StyleKey {
459 static TRANSITION_INFO: $crate::style::StyleKeyInfo = $crate::style::StyleKeyInfo::Transition;
460 static INFO: $crate::style::StyleKeyInfo = $crate::style::StyleKeyInfo::Prop($crate::style::StylePropInfo::new::<$name, $ty>(
461 prop!([impl inherited][$($options)*]),
462 || std::rc::Rc::new($crate::style::StyleMapValue::Val($name::default_value())),
463 $crate::style::StyleKey { info: &TRANSITION_INFO },
464 ));
465 $crate::style::StyleKey { info: &INFO }
466 }
467 fn default_value() -> Self::Type {
468 $default
469 }
470 }
471 };
472 ([impl inherited][inherited]) => {
473 true
474 };
475 ([impl inherited][]) => {
476 false
477 };
478}
479
480#[macro_export]
481macro_rules! prop_extractor {
482 (
483 $(#[$attrs:meta])* $vis:vis $name:ident {
484 $($prop_vis:vis $prop:ident: $reader:ty),*
485 $(,)?
486 }
487 ) => {
488 #[derive(Debug, Clone)]
489 $(#[$attrs])?
490 $vis struct $name {
491 $(
492 $prop_vis $prop: $crate::style::ExtractorField<$reader>,
493 )*
494 }
495
496 impl $name {
497 #[allow(dead_code)]
498 $vis fn read_style(&mut self, cx: &mut $crate::context::StyleCx, style: &$crate::style::Style) -> bool {
499 let mut transition = false;
500 let changed = false $(| self.$prop.read(style, style, &cx.now(), &mut transition))*;
501 if transition {
502 cx.request_transition();
503 }
504 changed
505 }
506
507 #[allow(dead_code)]
508 $vis fn read(&mut self, cx: &mut $crate::context::StyleCx) -> bool {
509 let mut transition = false;
510 let changed = self.read_explicit(&cx.direct_style(), &cx.indirect_style(), &cx.now(), &mut transition);
511 if transition {
512 cx.request_transition();
513 }
514 changed
515 }
516
517 #[allow(dead_code)]
518 $vis fn read_explicit(
519 &mut self,
520 style: &$crate::style::Style,
521 fallback: &$crate::style::Style,
522 #[cfg(not(target_arch = "wasm32"))]
523 now: &std::time::Instant,
524 #[cfg(target_arch = "wasm32")]
525 now: &web_time::Instant,
526 request_transition: &mut bool
527 ) -> bool {
528 false $(| self.$prop.read(style, fallback, now, request_transition))*
529 }
530
531 $($prop_vis fn $prop(&self) -> <$reader as $crate::style::StylePropReader>::Type
532 {
533 self.$prop.get()
534 })*
535 }
536
537 impl Default for $name {
538 fn default() -> Self {
539 Self {
540 $(
541 $prop: $crate::style::ExtractorField::new(),
542 )*
543 }
544 }
545 }
546 };
547}
548
549#[derive(Debug)]
554pub enum StyleKeyInfo {
555 Transition,
556 Prop(StylePropInfo),
557 Selector(StyleSelectors),
558 Class(StyleClassInfo),
559 ContextMappings,
561}
562
563pub(crate) static CONTEXT_MAPPINGS_INFO: StyleKeyInfo = StyleKeyInfo::ContextMappings;
564
565#[derive(Copy, Clone)]
566pub struct StyleKey {
567 pub info: &'static StyleKeyInfo,
568}
569
570impl StyleKey {
571 pub(crate) fn debug_any(&self, value: &dyn Any) -> String {
572 match self.info {
573 StyleKeyInfo::Selector(selectors) => selectors.debug_string(),
574 StyleKeyInfo::Transition | StyleKeyInfo::ContextMappings => String::new(),
575 StyleKeyInfo::Class(info) => (info.name)().to_string(),
576 StyleKeyInfo::Prop(v) => (v.debug_any)(value),
577 }
578 }
579 pub(crate) fn inherited(&self) -> bool {
580 match self.info {
581 StyleKeyInfo::Selector(..)
582 | StyleKeyInfo::Transition
583 | StyleKeyInfo::ContextMappings => false,
584 StyleKeyInfo::Class(..) => true,
585 StyleKeyInfo::Prop(v) => v.inherited,
586 }
587 }
588}
589
590impl PartialEq for StyleKey {
591 fn eq(&self, other: &Self) -> bool {
592 ptr::eq(self.info, other.info)
593 }
594}
595
596impl Hash for StyleKey {
597 fn hash<H: Hasher>(&self, state: &mut H) {
598 state.write_usize(self.info as *const _ as usize)
599 }
600}
601
602impl Eq for StyleKey {}
603
604impl Debug for StyleKey {
605 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
606 match self.info {
607 StyleKeyInfo::Selector(selectors) => {
608 write!(f, "selectors: {}", selectors.debug_string())
609 }
610 StyleKeyInfo::Transition => write!(f, "transition"),
611 StyleKeyInfo::ContextMappings => write!(f, "ContextMappings"),
612 StyleKeyInfo::Class(v) => write!(f, "{}", (v.name)()),
613 StyleKeyInfo::Prop(v) => write!(f, "{}", (v.name)()),
614 }
615 }
616}
617
618pub(crate) type ImHashMap<K, V> =
623 imbl::GenericHashMap<K, V, BuildHasherDefault<FxHasher>, DefaultSharedPtr>;