1use std::any::{Any, type_name};
12use std::fmt::{self, Debug};
13use std::hash::{Hash, Hasher};
14use std::ptr;
15use std::rc::Rc;
16
17#[cfg(not(target_arch = "wasm32"))]
18use std::time::Instant;
19#[cfg(target_arch = "wasm32")]
20use web_time::Instant;
21
22use crate::view::{IntoView, View};
23use crate::views::Label;
24
25use super::Style;
26use super::selectors::StyleSelectors;
27use super::transition::TransitionState;
28use super::values::{StyleMapValue, StylePropValue, StyleValue};
29
30pub trait StyleClass: Default + Copy + 'static {
35 fn key() -> StyleKey;
36 fn class_ref() -> StyleClassRef {
37 StyleClassRef { key: Self::key() }
38 }
39}
40
41pub trait StyleDebugGroup: Default + Copy + 'static {
42 fn key() -> StyleKey;
43 fn group_ref() -> StyleDebugGroupRef {
44 StyleDebugGroupRef { key: Self::key() }
45 }
46 fn member_props() -> Vec<StyleKey>;
47 fn debug_view(style: &Style) -> Option<Box<dyn View>>;
48}
49
50#[derive(Debug, Clone)]
51pub struct StyleClassInfo {
52 pub(crate) name: fn() -> &'static str,
53}
54
55impl StyleClassInfo {
56 pub const fn new<Name>() -> Self {
57 StyleClassInfo {
58 name: || std::any::type_name::<Name>(),
59 }
60 }
61}
62
63#[derive(Copy, Clone, Debug, PartialEq)]
64pub struct StyleClassRef {
65 pub key: StyleKey,
66}
67
68#[derive(Debug, Clone)]
69pub struct StyleDebugGroupInfo {
70 pub(crate) name: fn() -> &'static str,
71 pub(crate) inherited: bool,
72 pub(crate) member_props: fn() -> Vec<StyleKey>,
73 pub(crate) debug_view: fn(style: &Style) -> Option<Box<dyn View>>,
74}
75
76impl StyleDebugGroupInfo {
77 pub const fn new<Name>(
78 inherited: bool,
79 member_props: fn() -> Vec<StyleKey>,
80 debug_view: fn(style: &Style) -> Option<Box<dyn View>>,
81 ) -> Self {
82 StyleDebugGroupInfo {
83 name: || std::any::type_name::<Name>(),
84 inherited,
85 member_props,
86 debug_view,
87 }
88 }
89}
90
91#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
92pub struct StyleDebugGroupRef {
93 pub key: StyleKey,
94}
95
96macro_rules! style_key_selector {
97 ($v:vis $name:ident, $sel:expr) => {
98 fn $name() -> $crate::style::StyleKey {
99 static INFO: $crate::style::StyleKeyInfo = $crate::style::StyleKeyInfo::Selector($sel);
100 $crate::style::StyleKey { info: &INFO }
101 }
102 };
103}
104
105pub(crate) use style_key_selector;
106
107#[macro_export]
108macro_rules! style_class {
109 ($(#[$meta:meta])* $v:vis $name:ident) => {
110 $(#[$meta])*
111 #[derive(Default, Copy, Clone)]
112 $v struct $name;
113
114 impl $crate::style::StyleClass for $name {
115 fn key() -> $crate::style::StyleKey {
116 static INFO: $crate::style::StyleKeyInfo = $crate::style::StyleKeyInfo::Class(
117 $crate::style::StyleClassInfo::new::<$name>()
118 );
119 $crate::style::StyleKey { info: &INFO }
120 }
121 }
122 };
123}
124
125#[macro_export]
126macro_rules! style_debug_group {
127 ($(#[$meta:meta])* $v:vis $name:ident $(, inherited = $inherited:ident)?, members = [$($prop:ty),* $(,)?], view = $view:path) => {
128 $(#[$meta])*
129 #[derive(Default, Copy, Clone)]
130 $v struct $name;
131
132 impl $crate::style::StyleDebugGroup for $name {
133 fn key() -> $crate::style::StyleKey {
134 static INFO: $crate::style::StyleKeyInfo =
135 $crate::style::StyleKeyInfo::DebugGroup(
136 $crate::style::StyleDebugGroupInfo::new::<$name>(
137 style_debug_group!(@inherited $($inherited)?),
138 || vec![$(<$prop as $crate::style::StyleProp>::key()),*],
139 $view,
140 )
141 );
142 $crate::style::StyleKey { info: &INFO }
143 }
144
145 fn member_props() -> Vec<$crate::style::StyleKey> {
146 vec![$(<$prop as $crate::style::StyleProp>::key()),*]
147 }
148
149 fn debug_view(style: &$crate::style::Style) -> Option<Box<dyn $crate::view::View>> {
150 $view(style)
151 }
152 }
153 };
154 (@inherited inherited) => {
155 true
156 };
157 (@inherited) => {
158 false
159 };
160}
161
162pub trait StyleProp: Default + Copy + 'static {
167 type Type: StylePropValue;
168 fn key() -> StyleKey;
169 fn prop_ref() -> StylePropRef {
170 StylePropRef { key: Self::key() }
171 }
172 fn default_value() -> Self::Type;
173}
174
175pub(crate) type InterpolateFn =
176 fn(val1: &dyn Any, val2: &dyn Any, time: f64) -> Option<Rc<dyn Any>>;
177
178pub(crate) type HashAnyFn = fn(val: &dyn Any) -> u64;
180
181pub(crate) type EqAnyFn = fn(val1: &dyn Any, val2: &dyn Any) -> bool;
183pub(crate) type ResolveInheritedAnyFn = fn(val: &dyn Any, style: &Style) -> Rc<dyn Any>;
185
186#[derive(Debug)]
187pub struct StylePropInfo {
188 pub(crate) name: fn() -> &'static str,
189 pub(crate) inherited: bool,
190 #[allow(unused)]
191 pub(crate) default_as_any: fn() -> Rc<dyn Any>,
192 pub(crate) interpolate: InterpolateFn,
193 pub(crate) debug_any: fn(val: &dyn Any) -> String,
194 pub(crate) debug_view: fn(val: &dyn Any) -> Option<Box<dyn View>>,
195 pub(crate) transition_key: StyleKey,
196 pub(crate) hash_any: HashAnyFn,
198 pub(crate) eq_any: EqAnyFn,
200 pub(crate) resolve_inherited_any: ResolveInheritedAnyFn,
202}
203
204impl StylePropInfo {
205 pub const fn new<Name, T: StylePropValue + 'static>(
206 inherited: bool,
207 default_as_any: fn() -> Rc<dyn Any>,
208 transition_key: StyleKey,
209 ) -> Self {
210 StylePropInfo {
211 name: || std::any::type_name::<Name>(),
212 inherited,
213 default_as_any,
214 debug_any: |val| {
215 if let Some(v) = val.downcast_ref::<StyleMapValue<T>>() {
216 match v {
217 StyleMapValue::Val(v) | StyleMapValue::Animated(v) => format!("{v:?}"),
218 StyleMapValue::Context(_) => "Context(..)".to_owned(),
219 StyleMapValue::Unset => "Unset".to_owned(),
220 }
221 } else {
222 panic!(
223 "expected type {} for property {}",
224 type_name::<T>(),
225 std::any::type_name::<Name>(),
226 )
227 }
228 },
229 interpolate: |val1, val2, time| {
230 if let (Some(v1), Some(v2)) = (
231 val1.downcast_ref::<StyleMapValue<T>>(),
232 val2.downcast_ref::<StyleMapValue<T>>(),
233 ) {
234 if let (
235 StyleMapValue::Val(v1) | StyleMapValue::Animated(v1),
236 StyleMapValue::Val(v2) | StyleMapValue::Animated(v2),
237 ) = (v1, v2)
238 {
239 v1.interpolate(v2, time)
240 .map(|val| Rc::new(StyleMapValue::Animated(val)) as Rc<dyn Any>)
241 } else {
242 None
243 }
244 } else {
245 panic!(
246 "expected type {} for property {}. Got typeids {:?} and {:?}",
247 type_name::<T>(),
248 std::any::type_name::<Name>(),
249 val1.type_id(),
250 val2.type_id()
251 )
252 }
253 },
254 debug_view: |val| {
255 if let Some(v) = val.downcast_ref::<StyleMapValue<T>>() {
256 match v {
257 StyleMapValue::Val(v) | StyleMapValue::Animated(v) => v.debug_view(),
258 StyleMapValue::Context(_) => Some(Label::new("Context(..)").into_any()),
259 StyleMapValue::Unset => Some(Label::new("Unset").into_any()),
260 }
261 } else {
262 panic!(
263 "expected type {} for property {}",
264 type_name::<T>(),
265 std::any::type_name::<Name>(),
266 )
267 }
268 },
269 transition_key,
270 hash_any: |val| {
271 if let Some(v) = val.downcast_ref::<StyleMapValue<T>>() {
272 match v {
273 StyleMapValue::Val(v) | StyleMapValue::Animated(v) => v.content_hash(),
274 StyleMapValue::Context(_) => 1,
275 StyleMapValue::Unset => 0, }
277 } else {
278 panic!(
279 "expected type {} for property {}",
280 type_name::<T>(),
281 std::any::type_name::<Name>(),
282 )
283 }
284 },
285 eq_any: |val1, val2| {
286 if let (Some(v1), Some(v2)) = (
287 val1.downcast_ref::<StyleMapValue<T>>(),
288 val2.downcast_ref::<StyleMapValue<T>>(),
289 ) {
290 match (v1, v2) {
291 (
292 StyleMapValue::Val(a) | StyleMapValue::Animated(a),
293 StyleMapValue::Val(b) | StyleMapValue::Animated(b),
294 ) => a == b,
295 (StyleMapValue::Unset, StyleMapValue::Unset) => true,
296 _ => false,
297 }
298 } else {
299 panic!(
300 "expected type {} for property {}. Got typeids {:?} and {:?}",
301 type_name::<T>(),
302 std::any::type_name::<Name>(),
303 val1.type_id(),
304 val2.type_id()
305 )
306 }
307 },
308 resolve_inherited_any: |val, style| {
309 let resolved = match val.downcast_ref::<StyleMapValue<T>>().unwrap_or_else(|| {
310 panic!(
311 "expected type {} for property {}",
312 type_name::<T>(),
313 std::any::type_name::<Name>(),
314 )
315 }) {
316 StyleMapValue::Val(value) | StyleMapValue::Animated(value) => {
317 StyleMapValue::Val(value.clone())
318 }
319 StyleMapValue::Context(context_value) => {
320 StyleMapValue::Val(context_value.resolve(style))
321 }
322 StyleMapValue::Unset => StyleMapValue::Unset,
323 };
324 Rc::new(resolved)
325 },
326 }
327 }
328}
329
330#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
331pub struct StylePropRef {
332 pub key: StyleKey,
333}
334
335impl StylePropRef {
336 pub(crate) fn info(&self) -> &StylePropInfo {
337 if let StyleKeyInfo::Prop(prop) = self.key.info {
338 prop
339 } else {
340 panic!()
341 }
342 }
343}
344
345pub trait StylePropReader {
350 type State: Debug;
351 type Type: Clone;
352
353 fn read(
356 state: &mut Self::State,
357 style: &Style,
358 now: &Instant,
359 request_transition: &mut bool,
360 ) -> bool;
361
362 fn get(state: &Self::State) -> Self::Type;
363 fn new() -> Self::State;
364}
365
366impl<P: StyleProp> StylePropReader for P {
367 type State = (P::Type, TransitionState<P::Type>);
368 type Type = P::Type;
369
370 fn read(
372 state: &mut Self::State,
373 style: &Style,
374 now: &Instant,
375 request_transition: &mut bool,
376 ) -> bool {
377 let style_value = style.get_prop_style_value::<P>();
379 let mut prop_animated = false;
380 let new = match style_value {
381 StyleValue::Context(_) => {
382 unreachable!("context values should resolve during property reads")
383 }
384 StyleValue::Animated(val) => {
385 *request_transition = true;
386 prop_animated = true;
387 val
388 }
389 StyleValue::Val(val) => val,
390 StyleValue::Unset | StyleValue::Base => P::default_value(),
391 };
392 state.1.read(style.get_transition::<P>());
394
395 let changed = new != state.0;
397 if changed && !prop_animated {
398 state.1.transition(&Self::get(state), &new);
399 state.0 = new;
400 } else if prop_animated {
401 state.0 = new;
402 }
403 changed | state.1.step(now, request_transition)
404 }
405
406 fn get(state: &Self::State) -> Self::Type {
408 state.1.get(&state.0)
409 }
410
411 fn new() -> Self::State {
412 (P::default_value(), TransitionState::default())
413 }
414}
415
416impl<P: StyleProp> StylePropReader for Option<P> {
417 type State = Option<P::Type>;
418 type Type = Option<P::Type>;
419 fn read(
420 state: &mut Self::State,
421 style: &Style,
422 _now: &Instant,
423 _transition: &mut bool,
424 ) -> bool {
425 let new = style.get_prop::<P>();
426 let changed = new != *state;
427 *state = new;
428 changed
429 }
430 fn get(state: &Self::State) -> Self::Type {
431 state.clone()
432 }
433 fn new() -> Self::State {
434 None
435 }
436}
437
438#[derive(Clone)]
443pub struct ExtractorField<R: StylePropReader> {
444 state: R::State,
445}
446
447impl<R: StylePropReader> Debug for ExtractorField<R> {
448 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
449 self.state.fmt(f)
450 }
451}
452
453impl<R: StylePropReader> ExtractorField<R> {
454 pub fn read(&mut self, style: &Style, now: &Instant, request_transition: &mut bool) -> bool {
455 R::read(&mut self.state, style, now, request_transition)
456 }
457 pub fn get(&self) -> R::Type {
458 R::get(&self.state)
459 }
460 #[allow(clippy::new_without_default)]
461 pub fn new() -> Self {
462 Self { state: R::new() }
463 }
464}
465
466impl<R: StylePropReader> PartialEq for ExtractorField<R>
467where
468 R::Type: PartialEq,
469{
470 fn eq(&self, other: &Self) -> bool {
471 self.get() == other.get()
472 }
473}
474
475impl<R: StylePropReader> Eq for ExtractorField<R> where R::Type: Eq {}
476
477impl<R: StylePropReader> std::hash::Hash for ExtractorField<R>
478where
479 R::Type: std::hash::Hash,
480{
481 fn hash<H: Hasher>(&self, state: &mut H) {
482 self.get().hash(state)
483 }
484}
485
486#[macro_export]
491macro_rules! prop {
492 ($(#[$meta:meta])* $v:vis $name:ident: $ty:ty { $($options:tt)* } = $default:expr
493 ) => {
494 $(#[$meta])*
495 #[derive(Default, Copy, Clone)]
496 #[allow(missing_docs)]
497 $v struct $name;
498 impl $crate::style::StyleProp for $name {
499 type Type = $ty;
500 fn key() -> $crate::style::StyleKey {
501 static TRANSITION_INFO: $crate::style::StyleKeyInfo = $crate::style::StyleKeyInfo::Transition;
502 static INFO: $crate::style::StyleKeyInfo = $crate::style::StyleKeyInfo::Prop($crate::style::StylePropInfo::new::<$name, $ty>(
503 prop!([impl inherited][$($options)*]),
504 || std::rc::Rc::new($crate::style::StyleMapValue::Val($name::default_value())),
505 $crate::style::StyleKey { info: &TRANSITION_INFO },
506 ));
507 $crate::style::StyleKey { info: &INFO }
508 }
509 fn default_value() -> Self::Type {
510 $default
511 }
512 }
513 };
514 ([impl inherited][inherited]) => {
515 true
516 };
517 ([impl inherited][]) => {
518 false
519 };
520}
521
522#[macro_export]
523macro_rules! prop_extractor {
524 (
525 $(#[$attrs:meta])* $vis:vis $name:ident {
526 $($prop_vis:vis $prop:ident: $reader:ty),*
527 $(,)?
528 }
529 ) => {
530 #[derive(Debug, Clone)]
531 $(#[$attrs])?
532 $vis struct $name {
533 $(
534 $prop_vis $prop: $crate::style::ExtractorField<$reader>,
535 )*
536 }
537
538 impl $name {
539 #[allow(dead_code)]
540 $vis fn read_style(&mut self, cx: &mut $crate::context::StyleCx, style: &$crate::style::Style) -> bool {
541 self.read_style_for(cx, style, cx.current_view().get_element_id())
542 }
543
544 #[allow(dead_code)]
545 $vis fn read_style_for(
546 &mut self,
547 cx: &mut $crate::context::StyleCx,
548 style: &$crate::style::Style,
549 target: impl Into<$crate::ElementId>,
550 ) -> bool {
551 let mut transition = false;
552 let changed = false $(
553 | self.$prop.read(style, &cx.now(), &mut transition)
554 )*;
555 if transition {
556 cx.request_transition_for(target);
557 }
558 changed
559 }
560
561 #[allow(dead_code)]
562 $vis fn read(&mut self, cx: &mut $crate::context::StyleCx) -> bool {
563 self.read_for(cx, cx.current_view().get_element_id())
564 }
565
566 #[allow(dead_code)]
567 $vis fn read_for(
568 &mut self,
569 cx: &mut $crate::context::StyleCx,
570 target: impl Into<$crate::ElementId>,
571 ) -> bool {
572 let mut transition = false;
573 let changed = self.read_explicit(
574 &cx.direct_style(),
575 &cx.now(),
576 &mut transition,
577 );
578 if transition {
579 cx.request_transition_for(target);
580 }
581 changed
582 }
583
584 #[allow(dead_code)]
585 $vis fn read_explicit(
586 &mut self,
587 style: &$crate::style::Style,
588 #[cfg(not(target_arch = "wasm32"))]
589 now: &std::time::Instant,
590 #[cfg(target_arch = "wasm32")]
591 now: &web_time::Instant,
592 request_transition: &mut bool
593 ) -> bool {
594 false $(| self.$prop.read(style, now, request_transition))*
595 }
596
597 $($prop_vis fn $prop(&self) -> <$reader as $crate::style::StylePropReader>::Type
598 {
599 self.$prop.get()
600 })*
601 }
602
603 impl Default for $name {
604 fn default() -> Self {
605 Self {
606 $(
607 $prop: $crate::style::ExtractorField::new(),
608 )*
609 }
610 }
611 }
612 };
613}
614
615#[derive(Debug)]
620pub enum StyleKeyInfo {
621 Transition,
622 Prop(StylePropInfo),
623 Selector(StyleSelectors),
624 Class(StyleClassInfo),
625 DebugGroup(StyleDebugGroupInfo),
626 DeferredEffects,
627 StructuralSelectors,
629 ResponsiveSelectors,
631}
632
633pub(crate) static STRUCTURAL_SELECTORS_INFO: StyleKeyInfo = StyleKeyInfo::StructuralSelectors;
634pub(crate) static RESPONSIVE_SELECTORS_INFO: StyleKeyInfo = StyleKeyInfo::ResponsiveSelectors;
635
636#[derive(Copy, Clone)]
637pub struct StyleKey {
638 pub info: &'static StyleKeyInfo,
639}
640
641impl StyleKey {
642 pub(crate) fn debug_any(&self, value: &dyn Any) -> String {
643 match self.info {
644 StyleKeyInfo::Selector(selectors) => selectors.debug_string(),
645 StyleKeyInfo::Transition
646 | StyleKeyInfo::DebugGroup(_)
647 | StyleKeyInfo::DeferredEffects
648 | StyleKeyInfo::StructuralSelectors
649 | StyleKeyInfo::ResponsiveSelectors => String::new(),
650 StyleKeyInfo::Class(info) => (info.name)().to_string(),
651 StyleKeyInfo::Prop(v) => (v.debug_any)(value),
652 }
653 }
654 pub(crate) fn inherited(&self) -> bool {
655 match self.info {
656 StyleKeyInfo::Selector(..)
657 | StyleKeyInfo::Transition
658 | StyleKeyInfo::DeferredEffects
659 | StyleKeyInfo::StructuralSelectors
660 | StyleKeyInfo::ResponsiveSelectors => false,
661 StyleKeyInfo::Class(..) => true,
662 StyleKeyInfo::DebugGroup(v) => v.inherited,
663 StyleKeyInfo::Prop(v) => v.inherited,
664 }
665 }
666}
667
668impl PartialEq for StyleKey {
669 fn eq(&self, other: &Self) -> bool {
670 ptr::eq(self.info, other.info)
671 }
672}
673
674impl Hash for StyleKey {
675 fn hash<H: Hasher>(&self, state: &mut H) {
676 state.write_usize(self.info as *const _ as usize)
677 }
678}
679
680impl Eq for StyleKey {}
681
682impl Debug for StyleKey {
683 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
684 match self.info {
685 StyleKeyInfo::Selector(selectors) => {
686 write!(f, "selectors: {}", selectors.debug_string())
687 }
688 StyleKeyInfo::Transition => write!(f, "transition"),
689 StyleKeyInfo::DeferredEffects => write!(f, "DeferredEffects"),
690 StyleKeyInfo::StructuralSelectors => write!(f, "StructuralSelectors"),
691 StyleKeyInfo::ResponsiveSelectors => write!(f, "ResponsiveSelectors"),
692 StyleKeyInfo::Class(v) => write!(f, "{}", (v.name)()),
693 StyleKeyInfo::DebugGroup(v) => write!(f, "{}", (v.name)()),
694 StyleKeyInfo::Prop(v) => write!(f, "{}", (v.name)()),
695 }
696 }
697}