Skip to main content

fret_ui_kit/declarative/
pixelate.rs

1use fret_core::geometry::{Corners, Edges};
2use fret_core::scene::{EffectMode, EffectQuality};
3use fret_ui::element::{
4    AnyElement, ContainerProps, EffectLayerProps, LayoutStyle, Length, Overflow,
5};
6use fret_ui::{ElementContext, Theme, UiHost};
7
8use crate::recipes::pixelate::{
9    PixelateEffectRefinement, PixelateEffectTokenKeys, PixelateTokenKeys, pixelate_effect_chain,
10    resolve_pixelate_chrome, resolve_pixelate_effect,
11};
12use crate::recipes::resolve::ResolvedWithFallback;
13use crate::{ChromeRefinement, IntoUiElement, collect_children};
14
15#[derive(Debug, Clone)]
16pub struct PixelatePanelProps {
17    pub layout: LayoutStyle,
18    pub mode: EffectMode,
19    pub quality: EffectQuality,
20    pub chrome: ChromeRefinement,
21    pub chrome_keys: PixelateTokenKeys,
22    pub effect: PixelateEffectRefinement,
23    pub effect_keys: PixelateEffectTokenKeys,
24}
25
26impl Default for PixelatePanelProps {
27    fn default() -> Self {
28        let mut layout = LayoutStyle::default();
29        layout.size.width = Length::Fill;
30        layout.size.height = Length::Fill;
31        Self {
32            layout,
33            mode: EffectMode::FilterContent,
34            quality: EffectQuality::Auto,
35            chrome: ChromeRefinement::default(),
36            chrome_keys: PixelateTokenKeys::none(),
37            effect: PixelateEffectRefinement::default(),
38            effect_keys: PixelateEffectTokenKeys::none(),
39        }
40    }
41}
42
43pub fn pixelate_panel<H: UiHost, I, T>(
44    cx: &mut ElementContext<'_, H>,
45    props: PixelatePanelProps,
46    children: impl FnOnce(&mut ElementContext<'_, H>) -> I,
47) -> AnyElement
48where
49    I: IntoIterator<Item = T>,
50    T: IntoUiElement<H>,
51{
52    let theme = Theme::global(&*cx.app);
53    let chrome = resolve_pixelate_chrome(theme, &props.chrome, props.chrome_keys);
54    let effect = resolve_pixelate_effect(theme, &props.effect, props.effect_keys);
55    let chain = ResolvedWithFallback::ok(pixelate_effect_chain(effect));
56
57    let outer = ContainerProps {
58        layout: LayoutStyle {
59            overflow: Overflow::Clip,
60            ..props.layout
61        },
62        corner_radii: Corners::all(chrome.radius),
63        ..Default::default()
64    };
65
66    cx.container(outer, move |cx| {
67        let mut effect_layout = LayoutStyle::default();
68        effect_layout.size.width = Length::Fill;
69        effect_layout.size.height = Length::Fill;
70
71        let layer = cx.effect_layer_props(
72            EffectLayerProps {
73                layout: effect_layout,
74                mode: props.mode,
75                chain: chain.value,
76                quality: props.quality,
77            },
78            move |cx| {
79                let mut inner_layout = LayoutStyle::default();
80                inner_layout.size.width = Length::Fill;
81                inner_layout.size.height = Length::Fill;
82                let inner = ContainerProps {
83                    layout: inner_layout,
84                    padding: Edges::symmetric(chrome.padding_x, chrome.padding_y).into(),
85                    background: Some(chrome.background),
86                    border: Edges::all(chrome.border_width),
87                    border_color: Some(chrome.border_color),
88                    corner_radii: Corners::all(chrome.radius),
89                    ..Default::default()
90                };
91
92                let items = children(cx);
93                let inner_children = collect_children(cx, items);
94
95                vec![cx.container(inner, move |_cx| inner_children)]
96            },
97        );
98
99        vec![layer]
100    })
101}