1use crate::{
2 animate::Animation,
3 context::{
4 EventCallback, InteractionState, MenuCallback, MoveListener, ResizeCallback, ResizeListener,
5 },
6 event::EventListener,
7 pointer::PointerInputEvent,
8 prop_extractor,
9 responsive::ScreenSizeBp,
10 style::{
11 Background, BorderColor, BorderRadius, BoxShadowProp, LayoutProps, Outline, OutlineColor,
12 Style, StyleClassRef, StyleSelectors,
13 },
14};
15use bitflags::bitflags;
16use im::HashSet;
17use peniko::kurbo::{Affine, Point, Rect};
18use smallvec::SmallVec;
19use std::{cell::RefCell, collections::HashMap, marker::PhantomData, rc::Rc};
20use taffy::tree::NodeId;
21
22#[derive(Debug)]
24pub(crate) struct Stack<T> {
25 pub(crate) stack: SmallVec<[T; 1]>,
26}
27
28impl<T> Default for Stack<T> {
29 fn default() -> Self {
30 Stack {
31 stack: SmallVec::new(),
32 }
33 }
34}
35
36pub(crate) struct StackOffset<T> {
37 offset: usize,
38 phantom: PhantomData<T>,
39}
40
41impl<T> Clone for StackOffset<T> {
42 fn clone(&self) -> Self {
43 *self
44 }
45}
46
47impl<T> Copy for StackOffset<T> {}
48
49impl<T> Stack<T> {
50 pub fn next_offset(&mut self) -> StackOffset<T> {
51 StackOffset {
52 offset: self.stack.len(),
53 phantom: PhantomData,
54 }
55 }
56 pub fn push(&mut self, value: T) {
57 self.stack.push(value);
58 }
59 pub fn set(&mut self, offset: StackOffset<T>, value: T) {
60 self.stack[offset.offset] = value;
61 }
62
63 pub fn update(&mut self, offset: StackOffset<T>, update: impl Fn(&mut T) + 'static) {
64 update(&mut self.stack[offset.offset]);
65 }
66}
67
68prop_extractor! {
69 pub(crate) ViewStyleProps {
70 pub border_radius: BorderRadius,
71
72 pub outline: Outline,
73 pub outline_color: OutlineColor,
74 pub border_color: BorderColor,
75 pub background: Background,
76 pub shadow: BoxShadowProp,
77 }
78}
79
80bitflags! {
81 #[derive(Default, Copy, Clone, Debug)]
82 #[must_use]
83 pub(crate) struct ChangeFlags: u8 {
84 const STYLE = 1;
85 const LAYOUT = 1 << 1;
86 }
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90pub enum IsHiddenState {
91 Visible(taffy::style::Display),
92 AnimatingOut(taffy::style::Display),
93 Hidden,
94 None,
95}
96impl IsHiddenState {
97 pub(crate) fn get_display(&self) -> Option<taffy::style::Display> {
98 match self {
99 IsHiddenState::AnimatingOut(dis) => Some(*dis),
100 _ => None,
101 }
102 }
103
104 pub(crate) fn transition(
105 &mut self,
106 computed_display: taffy::Display,
107 remove_animations: impl FnOnce() -> bool,
108 add_animations: impl FnOnce(),
109 stop_reset_animations: impl FnOnce(),
110 num_waiting_anim: impl FnOnce() -> u16,
111 ) {
112 let computed_has_hide = computed_display == taffy::Display::None;
113 *self = match self {
114 Self::None if computed_has_hide => Self::Hidden,
116 Self::None if !computed_has_hide => Self::Visible(computed_display),
117 Self::Visible(dis) if !computed_has_hide => Self::Visible(*dis),
119 Self::Visible(dis) if computed_has_hide => {
121 let active_animations = remove_animations();
122 if active_animations {
123 Self::AnimatingOut(*dis)
124 } else {
125 Self::Hidden
126 }
127 }
128 Self::AnimatingOut(_) if !computed_has_hide => {
129 stop_reset_animations();
130 Self::Visible(computed_display)
131 }
132 Self::AnimatingOut(dis) if computed_has_hide => {
133 if num_waiting_anim() == 0 {
134 Self::Hidden
135 } else {
136 Self::AnimatingOut(*dis)
137 }
138 }
139 Self::Hidden if computed_has_hide => Self::Hidden,
140 Self::Hidden if !computed_has_hide => {
141 add_animations();
142 Self::Visible(computed_display)
143 }
144 _ => unreachable!(),
145 };
146 }
147}
148
149pub struct ViewState {
151 pub(crate) node: NodeId,
152 pub(crate) requested_changes: ChangeFlags,
153 pub(crate) style: Stack<Style>,
154 pub(crate) request_style_recursive: bool,
156 pub(crate) has_style_selectors: StyleSelectors,
157 pub(crate) viewport: Option<Rect>,
158 pub(crate) layout_rect: Rect,
159 pub(crate) layout_props: LayoutProps,
160 pub(crate) view_style_props: ViewStyleProps,
161 pub(crate) animations: Stack<Animation>,
162 pub(crate) classes: Vec<StyleClassRef>,
163 pub(crate) dragging_style: Option<Style>,
164 pub(crate) combined_style: Style,
165 pub(crate) taffy_style: taffy::style::Style,
166 pub(crate) event_listeners: HashMap<EventListener, Vec<Rc<RefCell<EventCallback>>>>,
167 pub(crate) context_menu: Option<Rc<MenuCallback>>,
168 pub(crate) popout_menu: Option<Rc<MenuCallback>>,
169 pub(crate) resize_listener: Option<Rc<RefCell<ResizeListener>>>,
170 pub(crate) window_origin: Point,
171 pub(crate) move_listener: Option<Rc<RefCell<MoveListener>>>,
172 pub(crate) cleanup_listener: Option<Rc<dyn Fn()>>,
173 pub(crate) last_pointer_down: Option<PointerInputEvent>,
174 pub(crate) is_hidden_state: IsHiddenState,
175 pub(crate) num_waiting_animations: u16,
176 pub(crate) disable_default_events: HashSet<EventListener>,
177 pub(crate) pointer_events: bool,
178 pub(crate) transform: Affine,
179 pub(crate) debug_name: SmallVec<[String; 1]>,
180}
181
182impl ViewState {
183 pub(crate) fn new(taffy: &mut taffy::TaffyTree) -> Self {
184 Self {
185 node: taffy.new_leaf(taffy::style::Style::DEFAULT).unwrap(),
186 viewport: None,
187 style: Default::default(),
188 layout_rect: Rect::ZERO,
189 layout_props: Default::default(),
190 view_style_props: Default::default(),
191 requested_changes: ChangeFlags::all(),
192 request_style_recursive: false,
193 has_style_selectors: StyleSelectors::default(),
194 animations: Default::default(),
195 classes: Vec::new(),
196 combined_style: Style::new(),
197 taffy_style: taffy::style::Style::DEFAULT,
198 dragging_style: None,
199 event_listeners: HashMap::new(),
200 context_menu: None,
201 popout_menu: None,
202 resize_listener: None,
203 move_listener: None,
204 cleanup_listener: None,
205 last_pointer_down: None,
206 window_origin: Point::ZERO,
207 is_hidden_state: IsHiddenState::None,
208 num_waiting_animations: 0,
209 disable_default_events: HashSet::new(),
210 pointer_events: true,
211 transform: Affine::IDENTITY,
212 debug_name: Default::default(),
213 }
214 }
215
216 #[allow(clippy::too_many_arguments)]
218 pub(crate) fn compute_style(
219 &mut self,
220 view_style: Option<Style>,
221 interact_state: InteractionState,
222 screen_size_bp: ScreenSizeBp,
223 view_class: Option<StyleClassRef>,
224 context: &Style,
225 ) -> bool {
226 let mut new_frame = false;
227 let mut computed_style = Style::new();
228 if let Some(view_style) = view_style {
229 computed_style.apply_mut(view_style);
230 }
231 if let Some(view_class) = view_class {
232 computed_style = computed_style.apply_classes_from_context(&[view_class], context);
233 }
234 computed_style = computed_style
235 .apply_classes_from_context(&self.classes, context)
236 .apply(self.style());
237
238 self.has_style_selectors = computed_style.selectors();
239
240 computed_style.apply_interact_state(&interact_state, screen_size_bp);
241
242 for animation in self
243 .animations
244 .stack
245 .iter_mut()
246 .filter(|anim| anim.can_advance() || anim.should_apply_folded())
247 {
248 if animation.can_advance() {
249 new_frame = true;
250
251 animation.animate_into(&mut computed_style);
252
253 animation.advance();
254 } else {
255 animation.apply_folded(&mut computed_style)
256 }
257 debug_assert!(!animation.is_idle());
258 }
259
260 self.combined_style = computed_style;
261
262 new_frame
263 }
264
265 pub(crate) fn has_active_animation(&self) -> bool {
266 for animation in self.animations.stack.iter() {
267 if animation.is_in_progress() {
268 return true;
269 }
270 }
271 false
272 }
273
274 pub(crate) fn style(&self) -> Style {
275 let mut result = Style::new();
276 for entry in self.style.stack.iter() {
277 result.apply_mut(entry.clone());
278 }
279 result
280 }
281
282 pub(crate) fn add_event_listener(
283 &mut self,
284 listener: EventListener,
285 action: Box<EventCallback>,
286 ) {
287 self.event_listeners
288 .entry(listener)
289 .or_default()
290 .push(Rc::new(RefCell::new(action)));
291 }
292
293 pub(crate) fn update_resize_listener(&mut self, action: Box<ResizeCallback>) {
294 self.resize_listener = Some(Rc::new(RefCell::new(ResizeListener {
295 rect: Rect::ZERO,
296 callback: action,
297 })));
298 }
299
300 pub(crate) fn update_move_listener(&mut self, action: Box<dyn Fn(Point)>) {
301 self.move_listener = Some(Rc::new(RefCell::new(MoveListener {
302 window_origin: Point::ZERO,
303 callback: action,
304 })));
305 }
306
307 pub(crate) fn update_cleanup_listener(&mut self, action: impl Fn() + 'static) {
308 self.cleanup_listener = Some(Rc::new(action));
309 }
310}