fission_core/ui/widgets/
image.rs1use crate::internal::InternalLower;
2use crate::lowering::{InternalIrBuilder, InternalLoweringCx};
3use fission_ir::{
4 op::{ImageFit, LayoutOp, Op, PaintOp},
5 WidgetId,
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<WidgetId>,
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 width(mut self, width: f32) -> Self {
89 self.width = Some(width);
90 self
91 }
92
93 pub fn height(mut self, height: f32) -> Self {
94 self.height = Some(height);
95 self
96 }
97
98 pub fn size(mut self, width: f32, height: f32) -> Self {
99 self.width = Some(width);
100 self.height = Some(height);
101 self
102 }
103
104 pub fn fit(mut self, fit: ImageFit) -> Self {
105 self.fit = fit;
106 self
107 }
108
109 pub fn alignment(mut self, alignment: ImageAlignment) -> Self {
110 self.alignment = alignment;
111 self
112 }
113
114 pub fn semantic_label(mut self, label: impl Into<String>) -> Self {
115 self.request.semantic_label = Some(label.into());
116 self
117 }
118
119 pub fn cache_size(mut self, width: u32, height: u32) -> Self {
120 self.request.cache_width = Some(width);
121 self.request.cache_height = Some(height);
122 self
123 }
124
125 pub fn loading(mut self, loading: ImageLoadingBehavior) -> Self {
126 self.request.loading = loading;
127 self
128 }
129
130 pub fn error(mut self, error: ImageErrorBehavior) -> Self {
131 self.request.error = error;
132 self
133 }
134
135 pub fn header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
136 if let ImageSource::Network { headers, .. } = &mut self.request.source {
137 headers.push(HttpHeader {
138 name: name.into(),
139 value: value.into(),
140 });
141 }
142 self
143 }
144
145 pub fn cache_policy(mut self, cache_policy: ImageCachePolicy) -> Self {
146 if let ImageSource::Network {
147 cache_policy: policy,
148 ..
149 } = &mut self.request.source
150 {
151 *policy = cache_policy;
152 }
153 self
154 }
155}
156
157impl InternalLower for Image {
158 fn lower(&self, cx: &mut InternalLoweringCx) -> WidgetId {
159 let layout_id = self.id.map(Into::into).unwrap_or_else(|| cx.next_node_id());
160 let paint_op = match &self.request.source {
161 ImageSource::SvgText { content } => PaintOp::DrawSvg {
162 content: content.clone(),
163 fill: None,
164 stroke: None,
165 },
166 _ => PaintOp::DrawImage {
167 request: self.request.clone(),
168 fit: self.fit,
169 alignment: self.alignment,
170 },
171 };
172 let paint_id = InternalIrBuilder::new(cx.next_node_id(), Op::Paint(paint_op)).build(cx);
173
174 let mut layout_builder = InternalIrBuilder::new(
175 layout_id,
176 Op::Layout(LayoutOp::Box {
177 width: self.width,
178 height: self.height,
179 min_width: None,
180 max_width: None,
181 min_height: None,
182 max_height: None,
183 padding: [0.0; 4],
184 flex_grow: 0.0,
185 flex_shrink: 0.0,
186 aspect_ratio: None,
187 }),
188 );
189 layout_builder.add_child(paint_id);
190 layout_builder.build(cx)
191 }
192}