fret_ui_kit/recipes/
pixelate.rs1use fret_core::{EffectChain, EffectStep, Px};
2use fret_ui::Theme;
3
4use crate::ChromeRefinement;
5use crate::recipes::effect_recipe::clamp_u32_from_metric;
6use crate::style::{ColorFallback, ColorRef, MetricFallback, MetricRef};
7
8#[derive(Debug, Clone, Copy)]
9pub struct PixelateTokenKeys {
10 pub padding_x: Option<&'static str>,
11 pub padding_y: Option<&'static str>,
12 pub radius: Option<&'static str>,
13 pub border_width: Option<&'static str>,
14 pub bg: Option<&'static str>,
15 pub border: Option<&'static str>,
16}
17
18impl PixelateTokenKeys {
19 pub const fn none() -> Self {
20 Self {
21 padding_x: None,
22 padding_y: None,
23 radius: None,
24 border_width: None,
25 bg: None,
26 border: None,
27 }
28 }
29}
30
31#[derive(Debug, Clone, Copy)]
32pub struct ResolvedPixelateChrome {
33 pub padding_x: Px,
34 pub padding_y: Px,
35 pub radius: Px,
36 pub border_width: Px,
37 pub background: fret_core::Color,
38 pub border_color: fret_core::Color,
39}
40
41pub fn resolve_pixelate_chrome(
42 theme: &Theme,
43 style: &ChromeRefinement,
44 keys: PixelateTokenKeys,
45) -> ResolvedPixelateChrome {
46 let default_padding_x = MetricRef::Token {
47 key: "component.pixelate.padding_x",
48 fallback: MetricFallback::ThemePaddingSm,
49 };
50 let default_padding_y = MetricRef::Token {
51 key: "component.pixelate.padding_y",
52 fallback: MetricFallback::ThemePaddingSm,
53 };
54 let default_radius = MetricRef::Token {
55 key: "component.pixelate.radius",
56 fallback: MetricFallback::ThemeRadiusSm,
57 };
58
59 let padding_x = style
60 .padding
61 .as_ref()
62 .and_then(|p| p.left.as_ref().or(p.right.as_ref()))
63 .map(|m| m.resolve(theme))
64 .or_else(|| keys.padding_x.and_then(|k| theme.metric_by_key(k)))
65 .or_else(|| theme.metric_by_key("component.pixelate.padding_x"))
66 .unwrap_or_else(|| default_padding_x.resolve(theme));
67 let padding_y = style
68 .padding
69 .as_ref()
70 .and_then(|p| p.top.as_ref().or(p.bottom.as_ref()))
71 .map(|m| m.resolve(theme))
72 .or_else(|| keys.padding_y.and_then(|k| theme.metric_by_key(k)))
73 .or_else(|| theme.metric_by_key("component.pixelate.padding_y"))
74 .unwrap_or_else(|| default_padding_y.resolve(theme));
75 let radius = style
76 .radius
77 .as_ref()
78 .map(|m| m.resolve(theme))
79 .or_else(|| keys.radius.and_then(|k| theme.metric_by_key(k)))
80 .or_else(|| theme.metric_by_key("component.pixelate.radius"))
81 .unwrap_or_else(|| default_radius.resolve(theme));
82 let border_width = style
83 .border_width
84 .as_ref()
85 .map(|m| m.resolve(theme))
86 .or_else(|| keys.border_width.and_then(|k| theme.metric_by_key(k)))
87 .or_else(|| theme.metric_by_key("component.pixelate.border_width"))
88 .unwrap_or(Px(1.0));
89
90 let default_bg = ColorRef::Token {
91 key: "component.pixelate.bg",
92 fallback: ColorFallback::ThemePanelBackground,
93 };
94 let default_border = ColorRef::Token {
95 key: "component.pixelate.border",
96 fallback: ColorFallback::ThemePanelBorder,
97 };
98
99 let background = style
100 .background
101 .as_ref()
102 .map(|c| c.resolve(theme))
103 .or_else(|| keys.bg.and_then(|k| theme.color_by_key(k)))
104 .or_else(|| theme.color_by_key("component.pixelate.bg"))
105 .unwrap_or_else(|| default_bg.resolve(theme));
106 let border_color = style
107 .border_color
108 .as_ref()
109 .map(|c| c.resolve(theme))
110 .or_else(|| keys.border.and_then(|k| theme.color_by_key(k)))
111 .or_else(|| theme.color_by_key("component.pixelate.border"))
112 .unwrap_or_else(|| default_border.resolve(theme));
113
114 ResolvedPixelateChrome {
115 padding_x: Px(padding_x.0.max(0.0)),
116 padding_y: Px(padding_y.0.max(0.0)),
117 radius: Px(radius.0.max(0.0)),
118 border_width: Px(border_width.0.max(0.0)),
119 background,
120 border_color,
121 }
122}
123
124#[derive(Debug, Clone, Default)]
125pub struct PixelateEffectRefinement {
126 pub scale: Option<u32>,
127}
128
129#[derive(Debug, Clone, Copy)]
130pub struct PixelateEffectTokenKeys {
131 pub scale: Option<&'static str>,
132}
133
134impl PixelateEffectTokenKeys {
135 pub const fn none() -> Self {
136 Self { scale: None }
137 }
138}
139
140#[derive(Debug, Clone, Copy)]
141pub struct ResolvedPixelateEffect {
142 pub scale: u32,
143}
144
145pub fn resolve_pixelate_effect(
146 theme: &Theme,
147 refinement: &PixelateEffectRefinement,
148 keys: PixelateEffectTokenKeys,
149) -> ResolvedPixelateEffect {
150 let scale = refinement.scale.unwrap_or_else(|| {
151 let metric = keys
152 .scale
153 .and_then(|k| theme.metric_by_key(k))
154 .or_else(|| theme.metric_by_key("component.pixelate.scale"))
155 .unwrap_or(Px(8.0));
156 clamp_u32_from_metric(metric, 1, 64, 8)
157 });
158
159 ResolvedPixelateEffect {
160 scale: scale.clamp(1, 256),
161 }
162}
163
164pub fn pixelate_effect_chain(effect: ResolvedPixelateEffect) -> EffectChain {
165 EffectChain::from_steps(&[EffectStep::Pixelate {
166 scale: effect.scale,
167 }])
168}