Skip to main content

lgui_core/core/view/
element.rs

1use std::{borrow::Cow, future::Future, sync::Arc};
2
3use super::{
4    async_handler, AnimationBinding, BackdropBlurStyle, Color, ComponentId, CompositingLayerSpec,
5    CustomPaintStyle, EventPolicy, HostTreeBuilder, IconStyle, ImageFit, ImageRequest,
6    InteractionRole, LayoutSpec, OverlayStyle, PathStyle, RenderPhase, ScrollRasterSpec,
7    StaticLayerSpec, TextStyle, UiAction, UiAsyncContext, UiEventContext, UiEventHandler,
8    UiEventKind, UiEventPayload, UiId, UiImageSource, UiInputEventHandler, UiNode, UiNodeKind,
9    UiPath, UiRect, VisualStyle,
10};
11
12#[derive(Clone)]
13pub struct UiElement {
14    node: UiNode,
15    children: Arc<Vec<UiElement>>,
16    component_boundary: Option<ComponentBoundary>,
17}
18
19#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20pub(crate) struct ComponentBoundary {
21    pub id: ComponentId,
22    pub retain_children: bool,
23}
24
25pub trait UiComponent {
26    fn render(self) -> UiElement;
27}
28
29impl UiComponent for UiElement {
30    fn render(self) -> UiElement {
31        self
32    }
33}
34
35impl UiElement {
36    pub fn new(id: UiId, kind: UiNodeKind, rect: UiRect) -> Self {
37        Self {
38            node: UiNode::new(id, kind, rect),
39            children: Arc::new(Vec::new()),
40            component_boundary: None,
41        }
42    }
43
44    pub(crate) fn estimated_bytes(&self) -> usize {
45        self.node
46            .estimated_bytes()
47            .saturating_add(
48                self.children
49                    .capacity()
50                    .saturating_mul(std::mem::size_of::<UiElement>()),
51            )
52            .saturating_add(
53                self.children
54                    .iter()
55                    .map(UiElement::estimated_bytes)
56                    .sum::<usize>(),
57            )
58    }
59
60    pub fn group(id: UiId, rect: UiRect) -> Self {
61        Self::new(id, UiNodeKind::Group, rect)
62    }
63
64    pub fn panel(id: UiId, rect: UiRect, style: VisualStyle) -> Self {
65        Self::new(id, UiNodeKind::Panel, rect).style(style)
66    }
67
68    pub fn button(id: UiId, rect: UiRect, style: VisualStyle) -> Self {
69        Self::new(id, UiNodeKind::Button, rect)
70            .style(style)
71            .interaction(InteractionRole::Button)
72    }
73
74    pub fn text(
75        id: UiId,
76        rect: UiRect,
77        text: impl Into<Cow<'static, str>>,
78        style: TextStyle,
79    ) -> Self {
80        Self::new(id, UiNodeKind::Text, rect).text_content(text, style)
81    }
82
83    pub fn custom(id: UiId, rect: UiRect, key: &'static str) -> Self {
84        Self::new(id, UiNodeKind::Custom(key), rect)
85    }
86
87    pub fn custom_paint(
88        id: UiId,
89        rect: UiRect,
90        key: &'static str,
91        style: CustomPaintStyle,
92    ) -> Self {
93        Self::custom(id, rect, key).custom_style(style)
94    }
95
96    pub fn image(id: UiId, rect: UiRect, source: &'static str, fit: ImageFit) -> Self {
97        Self::new(id, UiNodeKind::Image, rect).image_source(source, fit)
98    }
99
100    pub fn file_image(
101        id: UiId,
102        rect: UiRect,
103        source: impl Into<std::path::PathBuf>,
104        fit: ImageFit,
105    ) -> Self {
106        Self::new(id, UiNodeKind::Image, rect).image_source(UiImageSource::file(source), fit)
107    }
108
109    pub fn requested_image(
110        id: UiId,
111        rect: UiRect,
112        request: impl Into<ImageRequest>,
113        fit: ImageFit,
114    ) -> Self {
115        Self::new(id, UiNodeKind::Image, rect).image_request(request, fit)
116    }
117
118    pub fn icon(id: UiId, rect: UiRect, key: &'static str) -> Self {
119        Self::new(id, UiNodeKind::Icon, rect).icon_key(key)
120    }
121
122    pub fn glow(id: UiId, rect: UiRect, color: Color, alpha: u8) -> Self {
123        Self::new(id, UiNodeKind::Glow, rect).glow_effect(color, alpha)
124    }
125
126    pub fn backdrop_blur(id: UiId, rect: UiRect, style: BackdropBlurStyle) -> Self {
127        Self::new(id, UiNodeKind::BackdropBlur, rect).backdrop_blur_style(style)
128    }
129
130    pub fn backdrop_blur_path(
131        id: UiId,
132        rect: UiRect,
133        path: UiPath,
134        style: BackdropBlurStyle,
135    ) -> Self {
136        Self::new(id, UiNodeKind::BackdropBlurPath, rect)
137            .path_content(path, PathStyle::default())
138            .backdrop_blur_style(style)
139    }
140
141    pub fn overlay(id: UiId, rect: UiRect, style: OverlayStyle) -> Self {
142        Self::new(id, UiNodeKind::Overlay, rect).overlay_style(style)
143    }
144
145    pub fn static_layer(id: UiId, rect: UiRect, spec: StaticLayerSpec) -> Self {
146        Self::new(id, UiNodeKind::StaticLayer, rect).static_layer_spec(spec)
147    }
148
149    pub fn compositing_layer(id: UiId, rect: UiRect, spec: CompositingLayerSpec) -> Self {
150        Self::new(id, UiNodeKind::CompositingLayer, rect).compositing_layer_spec(spec)
151    }
152
153    pub fn scroll_raster(id: UiId, viewport: UiRect, spec: ScrollRasterSpec) -> Self {
154        Self::new(id, UiNodeKind::ScrollRaster, viewport).scroll_raster_spec(spec)
155    }
156
157    pub fn clip(id: UiId, rect: UiRect, offset_x: f32, offset_y: f32) -> Self {
158        Self::new(id, UiNodeKind::Clip, rect).clip_content(rect, offset_x, offset_y)
159    }
160
161    pub fn clip_path(id: UiId, rect: UiRect, path: UiPath) -> Self {
162        Self::new(id, UiNodeKind::ClipPath, rect).path_content(path, PathStyle::default())
163    }
164
165    pub fn line(id: UiId, rect: UiRect, style: VisualStyle) -> Self {
166        Self::new(id, UiNodeKind::Line, rect).style(style)
167    }
168
169    pub fn path(id: UiId, rect: UiRect, path: UiPath, style: PathStyle) -> Self {
170        Self::new(id, UiNodeKind::Path, rect).path_content(path, style)
171    }
172
173    pub fn ellipse(id: UiId, rect: UiRect, style: VisualStyle) -> Self {
174        Self::new(id, UiNodeKind::Ellipse, rect).style(style)
175    }
176
177    pub fn node(&self) -> &UiNode {
178        &self.node
179    }
180
181    pub fn children_ref(&self) -> &[UiElement] {
182        self.children.as_slice()
183    }
184
185    pub fn child(mut self, child: UiElement) -> Self {
186        Arc::make_mut(&mut self.children).push(child);
187        self
188    }
189
190    pub fn children(mut self, children: impl IntoIterator<Item = UiElement>) -> Self {
191        Arc::make_mut(&mut self.children).extend(children);
192        self
193    }
194
195    pub fn interaction(mut self, interaction: InteractionRole) -> Self {
196        self.node = self.node.interaction(interaction);
197        self
198    }
199
200    pub fn semantics(mut self, semantics: super::Semantics) -> Self {
201        self.node = self.node.semantics(semantics);
202        self
203    }
204
205    /// Marks this element as a native window drag region.
206    ///
207    /// Interactive descendants remain clickable and automatically take precedence
208    /// over the drag region during platform hit testing.
209    pub fn window_drag_region(mut self) -> Self {
210        self.node = self
211            .node
212            .interaction(InteractionRole::WindowDragRegion)
213            .event_policy(EventPolicy::NONE);
214        self
215    }
216
217    pub fn on_click<F>(mut self, handler: F) -> Self
218    where
219        F: Fn(&mut UiEventContext) + Send + Sync + 'static,
220    {
221        self.node = self.node.on_click(handler);
222        self
223    }
224
225    pub fn on_click_handler(mut self, handler: UiEventHandler) -> Self {
226        self.node = self.node.on_click_handler(handler);
227        self
228    }
229
230    pub fn on_click_async<F, Fut>(mut self, handler: F) -> Self
231    where
232        F: Fn(UiAsyncContext) -> Fut + Send + Sync + 'static,
233        Fut: Future<Output = ()> + Send + 'static,
234    {
235        self.node = self.node.on_click_handler(async_handler(handler));
236        self
237    }
238
239    pub fn on_click_capture<F>(mut self, handler: F) -> Self
240    where
241        F: Fn(&mut UiEventContext) + Send + Sync + 'static,
242    {
243        self.node = self.node.on_click_capture(handler);
244        self
245    }
246
247    pub fn on_event_handler(
248        mut self,
249        kind: UiEventKind,
250        capture: bool,
251        handler: UiInputEventHandler,
252    ) -> Self {
253        self.node = self.node.on_event_handler(kind, capture, handler);
254        self
255    }
256
257    pub fn on_event<F>(mut self, kind: UiEventKind, handler: F) -> Self
258    where
259        F: Fn(&mut UiEventContext, &UiEventPayload) + Send + Sync + 'static,
260    {
261        self.node = self.node.on_event(kind, handler);
262        self
263    }
264
265    pub fn on_event_capture<F>(mut self, kind: UiEventKind, handler: F) -> Self
266    where
267        F: Fn(&mut UiEventContext, &UiEventPayload) + Send + Sync + 'static,
268    {
269        self.node = self.node.on_event_capture(kind, handler);
270        self
271    }
272
273    pub fn on_click_capture_handler(mut self, handler: UiEventHandler) -> Self {
274        self.node = self.node.on_click_capture_handler(handler);
275        self
276    }
277
278    pub fn wheel_action(mut self, action: UiAction) -> Self {
279        self.node = self.node.wheel_action(action);
280        self
281    }
282
283    pub fn click_action(mut self, action: UiAction) -> Self {
284        self.node = self.node.click_action(action);
285        self
286    }
287
288    pub fn action_target(mut self, target: UiId) -> Self {
289        self.node = self.node.action_target(target);
290        self
291    }
292
293    pub fn on_action<F>(mut self, id: impl Into<super::ActionId>, handler: F) -> Self
294    where
295        F: Fn(&mut UiEventContext, &UiAction) + Send + Sync + 'static,
296    {
297        self.node = self.node.on_action(id, handler);
298        self
299    }
300
301    pub fn event_policy(mut self, policy: EventPolicy) -> Self {
302        self.node = self.node.event_policy(policy);
303        self
304    }
305
306    pub fn auto_focus(mut self) -> Self {
307        self.node = self.node.auto_focus(true);
308        self
309    }
310
311    pub fn focus_scope(mut self) -> Self {
312        self.node = self.node.focus_scope(true);
313        self
314    }
315
316    pub fn animation(mut self, binding: AnimationBinding) -> Self {
317        self.node = self.node.animation(binding);
318        self
319    }
320
321    pub fn animation_target(mut self, property: super::AnimProperty, active: bool) -> Self {
322        self.node = self.node.animation_target(property, active);
323        self
324    }
325
326    pub fn animation_outset(mut self, x: f32, y: f32) -> Self {
327        self.node = self.node.animation_outset(x, y);
328        self
329    }
330
331    pub fn layout(mut self, layout: LayoutSpec) -> Self {
332        self.node = self.node.layout(layout);
333        self
334    }
335
336    pub fn style(mut self, style: VisualStyle) -> Self {
337        self.node = self.node.style(style);
338        self
339    }
340
341    pub fn path_content(mut self, path: UiPath, style: PathStyle) -> Self {
342        self.node = self.node.path(path, style);
343        self
344    }
345
346    pub fn text_content(mut self, text: impl Into<Cow<'static, str>>, style: TextStyle) -> Self {
347        self.node = self.node.text(text, style);
348        self
349    }
350
351    pub fn image_source(mut self, source: impl Into<UiImageSource>, fit: ImageFit) -> Self {
352        self.node = self.node.image(source, fit);
353        self
354    }
355
356    pub fn image_request(mut self, request: impl Into<ImageRequest>, fit: ImageFit) -> Self {
357        self.node = self.node.image_request(request, fit);
358        self
359    }
360
361    pub fn icon_key(mut self, key: &'static str) -> Self {
362        self.node = self.node.icon(key);
363        self
364    }
365
366    pub fn icon_style(mut self, style: IconStyle) -> Self {
367        self.node = self.node.icon_style(style);
368        self
369    }
370
371    pub fn glow_effect(mut self, color: Color, alpha: u8) -> Self {
372        self.node = self.node.glow(color, alpha);
373        self
374    }
375
376    pub fn overlay_style(mut self, style: OverlayStyle) -> Self {
377        self.node = self.node.overlay(style);
378        self
379    }
380
381    pub fn backdrop_blur_style(mut self, style: BackdropBlurStyle) -> Self {
382        self.node = self.node.backdrop_blur(style);
383        self
384    }
385
386    pub fn custom_style(mut self, style: CustomPaintStyle) -> Self {
387        self.node = self.node.custom_style(style);
388        self
389    }
390
391    pub fn static_layer_spec(mut self, spec: StaticLayerSpec) -> Self {
392        self.node = self.node.static_layer(spec);
393        self
394    }
395
396    pub fn compositing_layer_spec(mut self, spec: CompositingLayerSpec) -> Self {
397        self.node = self.node.compositing_layer(spec);
398        self
399    }
400
401    /// Shadows this element and its subtree as one alpha silhouette, without changing layout.
402    pub fn shadow(mut self, style: super::ShadowStyle) -> Self {
403        self.node = self.node.shadow(style);
404        self
405    }
406
407    pub fn scroll_raster_spec(mut self, spec: ScrollRasterSpec) -> Self {
408        self.node = self.node.scroll_raster(spec);
409        self
410    }
411
412    pub fn clip_content(mut self, rect: UiRect, offset_x: f32, offset_y: f32) -> Self {
413        self.node = self.node.clip(rect, offset_x, offset_y);
414        self
415    }
416
417    pub fn hit_rect(mut self, rect: UiRect) -> Self {
418        self.node = self.node.hit_rect(rect);
419        self
420    }
421
422    pub fn paint_bounds(mut self, rect: UiRect) -> Self {
423        self.node = self.node.paint_bounds(rect);
424        self
425    }
426
427    pub fn ime_cursor_rect(mut self, rect: UiRect) -> Self {
428        self.node = self.node.ime_cursor_rect(rect);
429        self
430    }
431
432    pub fn render_phase(mut self, phase: RenderPhase) -> Self {
433        self.node = self.node.render_phase(phase);
434        self
435    }
436
437    pub fn translate(mut self, x: f32, y: f32) -> Self {
438        self.node = self.node.translate(x, y);
439        self.children = Arc::new(
440            self.children
441                .iter()
442                .cloned()
443                .map(|child| child.translate(x, y))
444                .collect(),
445        );
446        self
447    }
448
449    pub(crate) fn claim_component_owner(mut self, owner: ComponentId) -> Self {
450        if self.node.component_owner.is_none() {
451            self.node = self.node.component_owner(owner);
452        }
453        self.children = Arc::new(
454            self.children
455                .iter()
456                .cloned()
457                .map(|child| child.claim_component_owner(owner))
458                .collect(),
459        );
460        self
461    }
462
463    pub(crate) fn component_boundary(mut self, id: ComponentId) -> Self {
464        if self.component_boundary.is_none() {
465            self.component_boundary = Some(ComponentBoundary {
466                id,
467                retain_children: false,
468            });
469        }
470        self
471    }
472
473    pub(crate) fn into_retained_boundary(mut self, id: ComponentId) -> Self {
474        self.component_boundary = Some(ComponentBoundary {
475            id,
476            retain_children: true,
477        });
478        self
479    }
480
481    pub(crate) fn into_parts(self) -> (UiNode, Arc<Vec<UiElement>>, Option<ComponentBoundary>) {
482        (self.node, self.children, self.component_boundary)
483    }
484
485    pub fn mount(self, builder: &mut HostTreeBuilder) -> UiId {
486        builder.mount_element(self)
487    }
488}