paint/
web_content_animation.rs1use std::cell::{Cell, RefCell};
6use std::rc::Rc;
7use std::sync::Arc;
8use std::sync::atomic::{AtomicBool, Ordering};
9use std::time::Duration;
10
11use base::id::WebViewId;
12use embedder_traits::EventLoopWaker;
13use rustc_hash::FxHashMap;
14use servo_config::prefs;
15use webrender_api::{ColorF, PropertyBindingKey, PropertyValue};
16
17use crate::refresh_driver::TimerRefreshDriver;
18use crate::webview_renderer::WebViewRenderer;
19
20pub(crate) const CARET_BLINK_TIMEOUT: Duration = Duration::from_secs(30);
25
26pub(crate) struct WebContentAnimator {
33 event_loop_waker: Box<dyn EventLoopWaker>,
34 timer_refresh_driver: Rc<TimerRefreshDriver>,
35 caret_visible: Cell<bool>,
36 timer_scheduled: Cell<bool>,
37 need_update: Arc<AtomicBool>,
38}
39
40impl WebContentAnimator {
41 pub(crate) fn new(
42 event_loop_waker: Box<dyn EventLoopWaker>,
43 timer_refresh_driver: Rc<TimerRefreshDriver>,
44 ) -> Self {
45 Self {
46 event_loop_waker,
47 timer_refresh_driver,
48 caret_visible: Cell::new(true),
49 timer_scheduled: Default::default(),
50 need_update: Default::default(),
51 }
52 }
53
54 pub(crate) fn schedule_timer_if_necessary(&self) {
55 if self.timer_scheduled.get() {
56 return;
57 }
58
59 let Some(caret_blink_time) = prefs::get().editing_caret_blink_time() else {
60 return;
61 };
62
63 let event_loop_waker = self.event_loop_waker.clone();
64 let need_update = self.need_update.clone();
65 self.timer_refresh_driver.queue_timer(
66 caret_blink_time,
67 Box::new(move || {
68 need_update.store(true, Ordering::Relaxed);
69 event_loop_waker.wake();
70 }),
71 );
72 self.timer_scheduled.set(true);
73 }
74
75 pub(crate) fn update(
76 &self,
77 webview_renderers: &FxHashMap<WebViewId, WebViewRenderer>,
78 ) -> Option<Vec<PropertyValue<ColorF>>> {
79 if !self.need_update.load(Ordering::Relaxed) {
80 return None;
81 }
82
83 let mut colors = Vec::new();
84 for renderer in webview_renderers.values() {
85 renderer.for_each_connected_pipeline(&mut |pipeline_details| {
86 if let Some(property_value) =
87 pipeline_details.animations.update(self.caret_visible.get())
88 {
89 colors.push(property_value);
90 }
91 });
92 }
93
94 self.timer_scheduled.set(false);
95 self.need_update.store(false, Ordering::Relaxed);
96
97 if colors.is_empty() {
98 self.caret_visible.set(true);
101 return None;
102 }
103
104 self.caret_visible.set(!self.caret_visible.get());
105 self.schedule_timer_if_necessary();
106 Some(colors)
107 }
108}
109
110#[derive(Default)]
113pub(crate) struct PipelineAnimations {
114 caret: RefCell<Option<CaretAnimation>>,
115}
116
117impl PipelineAnimations {
118 pub(crate) fn update(&self, caret_visible: bool) -> Option<PropertyValue<ColorF>> {
119 let mut maybe_caret = self.caret.borrow_mut();
120 let caret = maybe_caret.as_mut()?;
121
122 if let Some(update) = caret.update(caret_visible) {
123 return Some(update);
124 }
125 *maybe_caret = None;
126 None
127 }
128
129 pub(crate) fn handle_new_display_list(
130 &self,
131 caret_property_binding: Option<(PropertyBindingKey<ColorF>, ColorF)>,
132 web_content_animator: &WebContentAnimator,
133 ) {
134 let Some(caret_blink_time) = prefs::get().editing_caret_blink_time() else {
135 return;
136 };
137
138 *self.caret.borrow_mut() = match caret_property_binding {
139 Some((caret_property_key, original_caret_color)) => {
140 web_content_animator.schedule_timer_if_necessary();
141 Some(CaretAnimation {
142 caret_property_key,
143 original_caret_color,
144 remaining_blink_count: (CARET_BLINK_TIMEOUT.as_millis() /
145 caret_blink_time.as_millis())
146 as usize,
147 })
148 },
149 None => None,
150 }
151 }
152}
153
154struct CaretAnimation {
156 pub caret_property_key: PropertyBindingKey<ColorF>,
157 pub original_caret_color: ColorF,
158 pub remaining_blink_count: usize,
159}
160
161impl CaretAnimation {
162 pub(crate) fn update(&mut self, caret_visible: bool) -> Option<PropertyValue<ColorF>> {
163 if self.remaining_blink_count == 0 {
164 return None;
165 }
166
167 self.remaining_blink_count = self.remaining_blink_count.saturating_sub(1);
168 let value = if caret_visible || self.remaining_blink_count == 0 {
169 self.original_caret_color
170 } else {
171 ColorF::TRANSPARENT
172 };
173
174 Some(PropertyValue {
175 key: self.caret_property_key,
176 value,
177 })
178 }
179}