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 embedder_traits::EventLoopWaker;
12use rustc_hash::FxHashMap;
13use servo_base::id::WebViewId;
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(&self, webview_renderers: &FxHashMap<WebViewId, WebViewRenderer>) -> bool {
78 if !self.need_update.load(Ordering::Relaxed) {
79 return false;
80 }
81
82 let mut anything_updated = false;
83 for renderer in webview_renderers.values() {
84 renderer.for_each_connected_pipeline(&mut |pipeline_details| {
85 anything_updated = pipeline_details.animations.update() || anything_updated;
86 });
87 }
88
89 self.timer_scheduled.set(false);
90 self.need_update.store(false, Ordering::Relaxed);
91
92 if !anything_updated {
93 self.caret_visible.set(true);
96 return false;
97 }
98
99 self.caret_visible.set(!self.caret_visible.get());
100 self.schedule_timer_if_necessary();
101 true
102 }
103
104 pub(crate) fn values(
105 &self,
106 webview_renderers: &FxHashMap<WebViewId, WebViewRenderer>,
107 ) -> Vec<PropertyValue<ColorF>> {
108 let mut colors = Vec::new();
109 for renderer in webview_renderers.values() {
110 renderer.for_each_connected_pipeline(&mut |pipeline_details| {
111 if let Some(property_value) =
112 pipeline_details.animations.values(self.caret_visible.get())
113 {
114 colors.push(property_value);
115 }
116 });
117 }
118 colors
119 }
120}
121
122#[derive(Default)]
125pub(crate) struct PipelineAnimations {
126 caret: RefCell<Option<CaretAnimation>>,
127}
128
129impl PipelineAnimations {
130 pub(crate) fn update(&self) -> bool {
133 let mut maybe_caret = self.caret.borrow_mut();
134 let Some(caret) = maybe_caret.as_mut() else {
135 return false;
136 };
137 caret.update();
138 if caret.done() {
139 *maybe_caret = None;
140 }
141 true
142 }
143
144 pub(crate) fn values(&self, caret_visible: bool) -> Option<PropertyValue<ColorF>> {
145 let maybe_caret = self.caret.borrow();
146 Some(maybe_caret.as_ref()?.value(caret_visible))
147 }
148
149 pub(crate) fn handle_new_display_list(
150 &self,
151 caret_property_binding: Option<(PropertyBindingKey<ColorF>, ColorF)>,
152 web_content_animator: &WebContentAnimator,
153 ) {
154 let Some(caret_blink_time) = prefs::get().editing_caret_blink_time() else {
155 return;
156 };
157
158 *self.caret.borrow_mut() = match caret_property_binding {
159 Some((caret_property_key, original_caret_color)) => {
160 web_content_animator.schedule_timer_if_necessary();
161 Some(CaretAnimation {
162 caret_property_key,
163 original_caret_color,
164 remaining_blink_count: (CARET_BLINK_TIMEOUT.as_millis() /
165 caret_blink_time.as_millis())
166 as usize,
167 })
168 },
169 None => None,
170 }
171 }
172}
173
174struct CaretAnimation {
176 pub caret_property_key: PropertyBindingKey<ColorF>,
177 pub original_caret_color: ColorF,
178 pub remaining_blink_count: usize,
179}
180
181impl CaretAnimation {
182 pub(crate) fn done(&self) -> bool {
183 self.remaining_blink_count == 0
184 }
185
186 pub(crate) fn update(&mut self) {
187 self.remaining_blink_count = self.remaining_blink_count.saturating_sub(1);
188 }
189
190 pub(crate) fn value(&self, caret_visible: bool) -> PropertyValue<ColorF> {
191 debug_assert!(self.remaining_blink_count != 0);
192 let value = if caret_visible {
193 self.original_caret_color
194 } else {
195 ColorF::TRANSPARENT
196 };
197 PropertyValue {
198 key: self.caret_property_key,
199 value,
200 }
201 }
202}