Skip to main content

lgui_core/core/view/declarative/
element.rs

1use super::*;
2
3// Declarative core shell only: this layer owns tree identity and composition,
4// but must not depend on concrete UI components. Component-specific adapters
5// belong beside the component they wrap during the migration.
6pub trait DeclarativeView {
7    fn compile(
8        self,
9        scope: &UiScope,
10        context: &UiRenderContext,
11        component_id: ComponentId,
12        force_components: bool,
13    ) -> UiElement;
14}
15
16pub trait IntoElementContent {
17    fn append_to(self, children: &mut Vec<Element>);
18}
19
20pub struct Fragment {
21    pub(super) children: Vec<Element>,
22}
23
24pub struct Element {
25    pub(super) key: ElementKey,
26    render: Box<dyn FnOnce(ElementRenderCx<'_, '_, '_>) -> UiElement>,
27    interaction: Option<InteractionRole>,
28    semantics: Option<Semantics>,
29    pub(super) click_capture_handler: Option<UiEventHandler>,
30    pub(super) click_handler: Option<UiEventHandler>,
31    pub(super) input_event_handlers: Vec<UiInputEventBinding>,
32    pub(super) event_policy: Option<EventPolicy>,
33    phase: Option<RenderPhase>,
34    animations: Vec<AnimationBinding>,
35    animation_targets: Vec<(AnimProperty, bool)>,
36    paint_bounds: Option<UiRect>,
37    shadow: Option<crate::core::ShadowStyle>,
38    focus_scope: bool,
39    defer_children: bool,
40    children: Vec<Element>,
41}
42
43#[derive(Clone, Copy)]
44pub struct ElementKey {
45    file: &'static str,
46    line: u32,
47    column: u32,
48    explicit: Option<u64>,
49}
50
51pub struct ElementRenderCx<'a, 'ctx, 'scope> {
52    pub id: UiId,
53    pub scope: &'scope UiScope,
54    pub context: &'ctx UiRenderContext<'a>,
55    pub children: Vec<UiElement>,
56    pub(super) component_id: ComponentId,
57    pub(super) force_components: bool,
58    deferred_children: Option<Vec<Element>>,
59    id_counter: Cell<u32>,
60}
61
62impl ElementRenderCx<'_, '_, '_> {
63    /// Returns a unique auto-generated child ID within the current scope.
64    /// Use this instead of manually calling `cx.scope.id(format!(...))`.
65    pub fn auto_id(&self) -> UiId {
66        let n = self.id_counter.get();
67        self.id_counter.set(n + 1);
68        self.scope.id(format!("{}._{}", self.id.as_str(), n))
69    }
70
71    pub fn use_context<T>(&self) -> T
72    where
73        T: Clone + 'static,
74    {
75        self.try_use_context::<T>().unwrap_or_else(|| {
76            panic!(
77                "missing context value `{}` for element `{}`",
78                type_name::<T>(),
79                self.id.as_str()
80            )
81        })
82    }
83
84    pub fn try_use_context<T>(&self) -> Option<T>
85    where
86        T: Clone + 'static,
87    {
88        self.context.contexts().read(self.component_id)
89    }
90
91    #[doc(hidden)]
92    pub fn component_owner(&self) -> (ComponentId, bool) {
93        (self.component_id, self.force_components)
94    }
95}
96
97impl Element {
98    #[track_caller]
99    pub fn new(render: impl FnOnce(ElementRenderCx<'_, '_, '_>) -> UiElement + 'static) -> Self {
100        Self::with_key(ElementKey::caller(), render)
101    }
102
103    pub fn with_key(
104        key: ElementKey,
105        render: impl FnOnce(ElementRenderCx<'_, '_, '_>) -> UiElement + 'static,
106    ) -> Self {
107        Self {
108            key,
109            render: Box::new(render),
110            interaction: None,
111            semantics: None,
112            click_capture_handler: None,
113            click_handler: None,
114            input_event_handlers: Vec::new(),
115            event_policy: None,
116            phase: None,
117            animations: Vec::new(),
118            animation_targets: Vec::new(),
119            paint_bounds: None,
120            shadow: None,
121            focus_scope: false,
122            defer_children: false,
123            children: Vec::new(),
124        }
125    }
126
127    pub fn child(mut self, child: impl Into<Element>) -> Self {
128        self.children.push(child.into());
129        self
130    }
131
132    pub fn content(mut self, content: impl IntoElementContent) -> Self {
133        content.append_to(&mut self.children);
134        self
135    }
136
137    pub fn children(mut self, children: impl IntoIterator<Item = impl Into<Element>>) -> Self {
138        self.children.extend(children.into_iter().map(Into::into));
139        self
140    }
141
142    pub fn key(mut self, key: impl AsRef<str>) -> Self {
143        self.key.explicit = Some(stable_hash(key.as_ref()));
144        self
145    }
146
147    #[doc(hidden)]
148    pub fn source_key(mut self, key: ElementKey) -> Self {
149        self.key = key;
150        self
151    }
152
153    pub fn interaction(mut self, interaction: InteractionRole) -> Self {
154        self.interaction = Some(interaction);
155        self
156    }
157
158    pub fn semantics(mut self, semantics: Semantics) -> Self {
159        self.semantics = Some(semantics);
160        self
161    }
162
163    /// Marks this element as a native window drag region.
164    ///
165    /// Interactive descendants are excluded automatically by hit testing.
166    pub fn window_drag_region(mut self) -> Self {
167        self.interaction = Some(InteractionRole::WindowDragRegion);
168        self.event_policy = Some(EventPolicy::NONE);
169        self
170    }
171
172    pub fn phase(mut self, phase: RenderPhase) -> Self {
173        self.phase = Some(phase);
174        self
175    }
176
177    pub fn animation(mut self, binding: AnimationBinding) -> Self {
178        self.animations.push(binding);
179        self
180    }
181
182    pub fn animation_target(mut self, property: AnimProperty, active: bool) -> Self {
183        self.animation_targets.push((property, active));
184        self
185    }
186
187    pub fn paint_bounds(mut self, rect: UiRect) -> Self {
188        self.paint_bounds = Some(rect);
189        self
190    }
191
192    /// Applies one shadow to the composited element and subtree. Ancestor clips still apply.
193    pub fn shadow(mut self, style: crate::core::ShadowStyle) -> Self {
194        self.shadow = Some(style);
195        self
196    }
197
198    pub fn focus_scope(mut self) -> Self {
199        self.focus_scope = true;
200        self
201    }
202
203    pub fn defer_children_compile(mut self) -> Self {
204        self.defer_children = true;
205        self
206    }
207
208    fn compile_internal(
209        self,
210        scope: &UiScope,
211        context: &UiRenderContext,
212        component_id: ComponentId,
213        force_components: bool,
214    ) -> UiElement {
215        let _current_context = context.contexts().enter_current(component_id);
216        let (children, deferred_children) = if self.defer_children {
217            (Vec::new(), Some(self.children))
218        } else {
219            (
220                compile_children(
221                    scope,
222                    context,
223                    component_id,
224                    force_components,
225                    self.children,
226                ),
227                None,
228            )
229        };
230        let mut element = (self.render)(ElementRenderCx {
231            id: scope.node_id(),
232            scope,
233            context,
234            children,
235            component_id,
236            force_components,
237            deferred_children,
238            id_counter: Cell::new(0),
239        });
240        if let Some(interaction) = self.interaction {
241            element = element.interaction(interaction);
242        }
243        if let Some(semantics) = self.semantics {
244            element = element.semantics(semantics);
245        }
246        if let Some(handler) = self.click_capture_handler {
247            element = element.on_click_capture_handler(handler);
248        }
249        if let Some(handler) = self.click_handler {
250            element = element.on_click_handler(handler);
251        }
252        for binding in self.input_event_handlers {
253            element = element.on_event_handler(binding.kind, binding.capture, binding.handler);
254        }
255        if let Some(policy) = self.event_policy {
256            element = element.event_policy(policy);
257        }
258        if let Some(phase) = self.phase {
259            element = element.render_phase(phase);
260        }
261        for animation in self.animations {
262            element = element.animation(animation);
263        }
264        for (property, active) in self.animation_targets {
265            element = element.animation_target(property, active);
266        }
267        if let Some(bounds) = self.paint_bounds {
268            element = element.paint_bounds(bounds);
269        }
270        if self.focus_scope {
271            element = element.focus_scope();
272        }
273        if let Some(shadow) = self.shadow {
274            element = element.shadow(shadow);
275        }
276        element
277    }
278}
279
280impl ElementRenderCx<'_, '_, '_> {
281    pub fn animation_value(&self, property: AnimProperty) -> f32 {
282        self.context.animation_value(&self.id, property)
283    }
284
285    pub fn compile(&self, element: Element) -> UiElement {
286        element.compile_internal(
287            self.scope,
288            self.context,
289            self.component_id,
290            self.force_components,
291        )
292    }
293
294    pub fn compile_deferred_children(&mut self) -> Vec<UiElement> {
295        self.deferred_children
296            .take()
297            .map(|children| {
298                compile_children(
299                    self.scope,
300                    self.context,
301                    self.component_id,
302                    self.force_components,
303                    children,
304                )
305            })
306            .unwrap_or_default()
307    }
308}
309
310impl DeclarativeView for Element {
311    fn compile(
312        self,
313        scope: &UiScope,
314        context: &UiRenderContext,
315        component_id: ComponentId,
316        force_components: bool,
317    ) -> UiElement {
318        self.compile_internal(scope, context, component_id, force_components)
319    }
320}
321
322impl ElementKey {
323    #[track_caller]
324    pub fn caller() -> Self {
325        let location = Location::caller();
326        Self {
327            file: location.file(),
328            line: location.line(),
329            column: location.column(),
330            explicit: None,
331        }
332    }
333
334    pub(super) fn segment(self, index: usize) -> String {
335        match self.explicit {
336            Some(key) => format!("e.{:016x}.k.{key:016x}", self.hash()),
337            None => format!("e.{:016x}.{index}", self.hash()),
338        }
339    }
340
341    fn hash(self) -> u64 {
342        let mut hash = FNV_OFFSET;
343        hash = hash_bytes(hash, self.file.as_bytes());
344        hash = hash_u32(hash, self.line);
345        hash_u32(hash, self.column)
346    }
347}
348
349impl Element {
350    pub(super) fn with_layout(mut self, layout: LayoutSpec) -> Self {
351        let render = self.render;
352        self.render = Box::new(move |cx| render(cx).layout(layout));
353        self
354    }
355}
356
357fn compile_children(
358    scope: &UiScope,
359    context: &UiRenderContext,
360    component_id: ComponentId,
361    force_components: bool,
362    children: Vec<Element>,
363) -> Vec<UiElement> {
364    children
365        .into_iter()
366        .enumerate()
367        .map(|(index, child)| {
368            let child_scope = UiScope::from_path(scope.path().child(child.key.segment(index)));
369            child.compile_internal(&child_scope, context, component_id, force_components)
370        })
371        .collect()
372}
373
374fn hash_u32(hash: u64, value: u32) -> u64 {
375    hash_bytes(hash, &value.to_le_bytes())
376}
377
378fn hash_bytes(mut hash: u64, bytes: &[u8]) -> u64 {
379    for byte in bytes {
380        hash ^= u64::from(*byte);
381        hash = hash.wrapping_mul(FNV_PRIME);
382    }
383    hash
384}
385
386pub(super) fn stable_hash(value: &str) -> u64 {
387    hash_bytes(FNV_OFFSET, value.as_bytes())
388}
389
390pub(super) fn location_hash(location: &'static Location<'static>) -> u64 {
391    let mut hash = FNV_OFFSET;
392    hash = hash_bytes(hash, location.file().as_bytes());
393    hash = hash_u32(hash, location.line());
394    hash_u32(hash, location.column())
395}
396
397const FNV_OFFSET: u64 = 0xcbf29ce484222325;
398const FNV_PRIME: u64 = 0x100000001b3;