azul_layout/managers/
gpu_state.rs1use alloc::collections::BTreeMap;
9
10use azul_core::{
11 dom::{DomId, NodeId},
12 geom::LogicalSize,
13 gpu::{GpuEventChanges, GpuScrollbarOpacityEvent, GpuTransformKeyEvent, GpuValueCache},
14 resources::{OpacityKey, TransformKey},
15 task::{Duration, Instant, SystemTimeDiff},
16 transform::{ComputedTransform3D, RotationMode},
17};
18
19use crate::{
20 managers::scroll_state::{FrameScrollInfo, ScrollManager},
21 solver3::{layout_tree::LayoutTree, scrollbar::ScrollbarRequirements},
22 text3::cache::ParsedFontTrait,
23};
24
25pub const DEFAULT_FADE_DELAY_MS: u64 = 500;
27pub const DEFAULT_FADE_DURATION_MS: u64 = 200;
29
30#[derive(Debug, Clone)]
39pub struct GpuStateManager {
40 pub caches: BTreeMap<DomId, GpuValueCache>,
42 pub fade_delay: Duration,
44 pub fade_duration: Duration,
46}
47
48impl Default for GpuStateManager {
49 fn default() -> Self {
50 Self::new(
51 Duration::System(SystemTimeDiff::from_millis(DEFAULT_FADE_DELAY_MS)),
52 Duration::System(SystemTimeDiff::from_millis(DEFAULT_FADE_DURATION_MS)),
53 )
54 }
55}
56
57#[derive(Debug, Clone)]
62struct OpacityState {
63 current_value: f32,
65 target_value: f32,
67 last_activity_time: Instant,
69 transition_start_time: Option<Instant>,
71}
72
73#[derive(Debug, Default)]
78pub struct GpuTickResult {
79 pub needs_repaint: bool,
81 pub changes: GpuEventChanges,
83}
84
85impl GpuStateManager {
86 pub fn new(fade_delay: Duration, fade_duration: Duration) -> Self {
88 Self {
89 caches: BTreeMap::new(),
90 fade_delay,
91 fade_duration,
92 }
93 }
94
95 pub fn tick(&mut self, now: Instant) -> GpuTickResult {
100 GpuTickResult::default()
103 }
104
105 pub fn get_or_create_cache(&mut self, dom_id: DomId) -> &mut GpuValueCache {
107 self.caches.entry(dom_id).or_default()
108 }
109
110 pub fn update_scrollbar_transforms(
116 &mut self,
117 dom_id: DomId,
118 scroll_manager: &ScrollManager,
119 layout_tree: &LayoutTree,
120 ) -> GpuEventChanges {
121 let mut changes = GpuEventChanges::empty();
122 let gpu_cache = self.get_or_create_cache(dom_id);
123
124 for (node_idx, node) in layout_tree.nodes.iter().enumerate() {
125 let Some(scrollbar_info) = &node.scrollbar_info else {
126 continue;
127 };
128 let Some(node_id) = node.dom_node_id else {
129 continue;
130 };
131
132 let scroll_offset = scroll_manager
133 .get_current_offset(dom_id, node_id)
134 .unwrap_or_default();
135 let container_size = node.used_size.unwrap_or_default();
136 let content_size = node
137 .inline_layout_result
138 .as_ref()
139 .map(|l| {
140 let bounds = l.layout.bounds();
141 LogicalSize {
142 width: bounds.width,
143 height: bounds.height,
144 }
145 })
146 .unwrap_or(container_size);
147
148 if scrollbar_info.needs_vertical {
149 let transform = compute_vertical_thumb_transform(
150 scrollbar_info,
151 &container_size,
152 &content_size,
153 scroll_offset.y,
154 );
155
156 update_transform_key(gpu_cache, &mut changes, dom_id, node_id, transform);
157 }
158 }
159
160 changes
161 }
162
163 pub fn get_gpu_value_cache(&self) -> BTreeMap<DomId, GpuValueCache> {
165 self.caches.clone()
166 }
167}
168
169fn compute_vertical_thumb_transform(
171 scrollbar_info: &ScrollbarRequirements,
172 container_size: &LogicalSize,
173 content_size: &LogicalSize,
174 scroll_y: f32,
175) -> ComputedTransform3D {
176 let track_height = container_size.height - scrollbar_info.scrollbar_height;
177 let thumb_height = (container_size.height / content_size.height) * track_height;
178 let scrollable_dist = content_size.height - container_size.height;
179 let thumb_dist = track_height - thumb_height;
180
181 let scroll_ratio = if scrollable_dist > 0.0 {
182 scroll_y / scrollable_dist
183 } else {
184 0.0
185 };
186 let thumb_offset_y = scroll_ratio * thumb_dist;
187
188 ComputedTransform3D::new_translation(0.0, thumb_offset_y, 0.0)
189}
190
191fn update_transform_key(
193 gpu_cache: &mut GpuValueCache,
194 changes: &mut GpuEventChanges,
195 dom_id: DomId,
196 node_id: NodeId,
197 transform: ComputedTransform3D,
198) {
199 if let Some(existing_transform) = gpu_cache.current_transform_values.get(&node_id) {
200 if *existing_transform != transform {
201 let transform_key = gpu_cache.transform_keys[&node_id];
202 changes
203 .transform_key_changes
204 .push(GpuTransformKeyEvent::Changed(
205 node_id,
206 transform_key,
207 *existing_transform,
208 transform,
209 ));
210 gpu_cache
211 .current_transform_values
212 .insert(node_id, transform);
213 }
214 } else {
215 let transform_key = TransformKey::unique();
216 gpu_cache.transform_keys.insert(node_id, transform_key);
217 gpu_cache
218 .current_transform_values
219 .insert(node_id, transform);
220 changes
221 .transform_key_changes
222 .push(GpuTransformKeyEvent::Added(
223 node_id,
224 transform_key,
225 transform,
226 ));
227 }
228}