ferroui_base/styling/
control_theme.rs1use super::{DuplicateSetterError, SelectorMatchResult, Setter, StyleBase, StyleBaseImpl};
2use crate::property_store::FrameType;
3use crate::{ferro_class, instantiate, FerroObjectImpl, ObjectType, Ref, StyledElement, TypeInfo};
4use std::cell::{Cell, RefCell};
5use std::rc::Rc;
6
7#[repr(C)]
9pub struct ControlTheme {
10 base: StyleBase,
11 target_type: Cell<Option<&'static TypeInfo>>,
12 based_on: RefCell<Option<Ref<ControlTheme>>>,
13}
14
15ferro_class!(ControlTheme: StyleBase);
16crate::ferro_class_info!(ControlTheme { new: ControlTheme::new });
17
18impl FerroObjectImpl for ControlTheme {}
19
20impl StyleBaseImpl for ControlTheme {
21 fn set_parent(_this: &Self, _parent: Option<&Ref<StyleBase>>) {
22 panic!("ControlThemes cannot be added as a nested style.");
23 }
24
25 fn to_display_string(this: &Self) -> String {
26 match this.target_type.get() {
27 Some(target_type) => target_type.name().to_string(),
28 None => "ControlTheme".to_string(),
29 }
30 }
31}
32
33impl ControlTheme {
34 pub fn construct() -> Self {
36 Self { base: StyleBase::construct(), target_type: Cell::new(None), based_on: RefCell::new(None) }
37 }
38
39 pub fn new() -> Ref<Self> {
41 instantiate(Self::construct())
42 }
43
44 pub fn with_target_type(target_type: &'static TypeInfo) -> Ref<Self> {
46 let theme = Self::new();
47 theme.set_target_type(Some(target_type));
48 theme
49 }
50
51 pub fn for_type<T: ObjectType>() -> Ref<Self> {
53 Self::with_target_type(T::TYPE)
54 }
55
56 pub fn target_type(&self) -> Option<&'static TypeInfo> {
58 self.target_type.get()
59 }
60
61 pub fn set_target_type(&self, value: Option<&'static TypeInfo>) {
62 self.target_type.set(value)
63 }
64
65 pub fn based_on(&self) -> Option<Ref<ControlTheme>> {
67 self.based_on.borrow().clone()
68 }
69
70 pub fn set_based_on(&self, value: Option<Ref<ControlTheme>>) {
71 *self.based_on.borrow_mut() = value;
72 }
73
74 pub fn with_setters(
76 target_type: &'static TypeInfo,
77 setters: impl IntoIterator<Item = Rc<Setter>>,
78 ) -> Ref<Self> {
79 let theme = Self::with_target_type(target_type);
80 for setter in setters {
81 theme.add_setter(setter);
82 }
83 theme
84 }
85
86 #[allow(dead_code)]
89 pub(crate) fn try_attach(&self, target: &StyledElement, type_: FrameType) -> SelectorMatchResult {
90 match self.try_attach_checked(target, type_) {
91 Ok(result) => result,
92 Err(error) => panic!("{error}"),
93 }
94 }
95
96 pub(crate) fn try_attach_checked(
99 &self,
100 target: &StyledElement,
101 type_: FrameType,
102 ) -> Result<SelectorMatchResult, DuplicateSetterError> {
103 debug_assert!(matches!(type_, FrameType::Theme | FrameType::TemplatedParentTheme));
104
105 let Some(target_type) = self.target_type.get() else {
106 panic!("ControlTheme has no TargetType.");
107 };
108 crate::perf_count!(ControlThemesEvaluated);
109
110 if self.has_setters_or_animations() && target_type.is_assignable_from(target.style_key()) {
111 crate::perf_count!(ControlThemesMatched);
112 self.try_attach_instance(target, None, type_, true)?;
113 return Ok(SelectorMatchResult::AlwaysThisType);
114 }
115
116 Ok(SelectorMatchResult::NeverThisType)
117 }
118}