fission_core/ui/widgets/
image.rs1use crate::lowering::{LoweringContext, NodeBuilder};
2use crate::ui::traits::Lower;
3use fission_ir::{
4 op::{ImageFit, LayoutOp, Op, PaintOp},
5 NodeId,
6};
7use serde::{Deserialize, Serialize};
8
9pub use fission_ir::op::{
10 HttpHeader, ImageAlignment, ImageCachePolicy, ImageErrorBehavior, ImageLoadingBehavior,
11 ImageRequest, ImageSource,
12};
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct Image {
21 pub id: Option<NodeId>,
23 pub request: ImageRequest,
25 pub width: Option<f32>,
27 pub height: Option<f32>,
29 pub fit: ImageFit,
31 pub alignment: ImageAlignment,
33}
34
35impl Default for Image {
36 fn default() -> Self {
37 Self {
38 id: None,
39 request: ImageRequest::default(),
40 width: None,
41 height: None,
42 fit: ImageFit::Contain,
43 alignment: ImageAlignment::Center,
44 }
45 }
46}
47
48impl Image {
49 pub fn asset(path: impl Into<String>) -> Self {
50 Self::from_source(ImageSource::Asset { path: path.into() })
51 }
52
53 pub fn file(path: impl Into<String>) -> Self {
54 Self::from_source(ImageSource::File { path: path.into() })
55 }
56
57 pub fn network(url: impl Into<String>) -> Self {
58 Self::from_source(ImageSource::Network {
59 url: url.into(),
60 headers: Vec::new(),
61 cache_policy: ImageCachePolicy::Default,
62 })
63 }
64
65 pub fn memory(bytes: impl Into<Vec<u8>>) -> Self {
66 Self::from_source(ImageSource::Memory {
67 bytes: bytes.into(),
68 mime_type: None,
69 })
70 }
71
72 pub fn svg_text(content: impl Into<String>) -> Self {
73 Self::from_source(ImageSource::SvgText {
74 content: content.into(),
75 })
76 }
77
78 pub fn from_source(source: ImageSource) -> Self {
79 Self {
80 request: ImageRequest {
81 source,
82 ..Default::default()
83 },
84 ..Default::default()
85 }
86 }
87
88 pub fn id(mut self, id: NodeId) -> Self {
89 self.id = Some(id);
90 self
91 }
92
93 pub fn width(mut self, width: f32) -> Self {
94 self.width = Some(width);
95 self
96 }
97
98 pub fn height(mut self, height: f32) -> Self {
99 self.height = Some(height);
100 self
101 }
102
103 pub fn size(mut self, width: f32, height: f32) -> Self {
104 self.width = Some(width);
105 self.height = Some(height);
106 self
107 }
108
109 pub fn fit(mut self, fit: ImageFit) -> Self {
110 self.fit = fit;
111 self
112 }
113
114 pub fn alignment(mut self, alignment: ImageAlignment) -> Self {
115 self.alignment = alignment;
116 self
117 }
118
119 pub fn semantic_label(mut self, label: impl Into<String>) -> Self {
120 self.request.semantic_label = Some(label.into());
121 self
122 }
123
124 pub fn cache_size(mut self, width: u32, height: u32) -> Self {
125 self.request.cache_width = Some(width);
126 self.request.cache_height = Some(height);
127 self
128 }
129
130 pub fn loading(mut self, loading: ImageLoadingBehavior) -> Self {
131 self.request.loading = loading;
132 self
133 }
134
135 pub fn error(mut self, error: ImageErrorBehavior) -> Self {
136 self.request.error = error;
137 self
138 }
139
140 pub fn header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
141 if let ImageSource::Network { headers, .. } = &mut self.request.source {
142 headers.push(HttpHeader {
143 name: name.into(),
144 value: value.into(),
145 });
146 }
147 self
148 }
149
150 pub fn cache_policy(mut self, cache_policy: ImageCachePolicy) -> Self {
151 if let ImageSource::Network {
152 cache_policy: policy,
153 ..
154 } = &mut self.request.source
155 {
156 *policy = cache_policy;
157 }
158 self
159 }
160
161 pub fn into_node(self) -> crate::ui::Node {
162 crate::ui::Node::Image(self)
163 }
164}
165
166impl Lower for Image {
167 fn lower(&self, cx: &mut LoweringContext) -> NodeId {
168 let layout_id = self.id.unwrap_or_else(|| cx.next_node_id());
169 let paint_op = match &self.request.source {
170 ImageSource::SvgText { content } => PaintOp::DrawSvg {
171 content: content.clone(),
172 fill: None,
173 stroke: None,
174 },
175 _ => PaintOp::DrawImage {
176 request: self.request.clone(),
177 fit: self.fit,
178 alignment: self.alignment,
179 },
180 };
181 let paint_id = NodeBuilder::new(cx.next_node_id(), Op::Paint(paint_op)).build(cx);
182
183 let mut layout_builder = NodeBuilder::new(
184 layout_id,
185 Op::Layout(LayoutOp::Box {
186 width: self.width,
187 height: self.height,
188 min_width: None,
189 max_width: None,
190 min_height: None,
191 max_height: None,
192 padding: [0.0; 4],
193 flex_grow: 0.0,
194 flex_shrink: 0.0,
195 aspect_ratio: None,
196 }),
197 );
198 layout_builder.add_child(paint_id);
199 layout_builder.build(cx)
200 }
201}