1use std::{
2 borrow::Cow,
3 fmt::Debug,
4 io::Cursor,
5 pin::Pin,
6};
7
8use bytes::Bytes;
9use freya_core::{
10 integration::*,
11 prelude::Color,
12};
13use image::ImageReader;
14use winit::{
15 event_loop::ActiveEventLoop,
16 window::{
17 Icon,
18 Window,
19 WindowAttributes,
20 },
21};
22
23use crate::plugins::{
24 FreyaPlugin,
25 PluginsManager,
26};
27
28pub type WindowBuilderHook =
29 Box<dyn FnOnce(WindowAttributes, &ActiveEventLoop) -> WindowAttributes + Send + Sync>;
30pub type WindowHandleHook = Box<dyn FnOnce(&mut Window) + Send + Sync>;
31
32pub struct WindowConfig {
34 pub(crate) app: FpRender,
36 pub(crate) size: (f64, f64),
38 pub(crate) min_size: Option<(f64, f64)>,
40 pub(crate) max_size: Option<(f64, f64)>,
42 pub(crate) decorations: bool,
44 pub(crate) title: &'static str,
46 pub(crate) transparent: bool,
48 pub(crate) background: Color,
50 pub(crate) resizable: bool,
52 pub(crate) icon: Option<Icon>,
54 pub(crate) window_attributes_hook: Option<WindowBuilderHook>,
56 pub(crate) window_handle_hook: Option<WindowHandleHook>,
58}
59
60impl Debug for WindowConfig {
61 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62 f.debug_struct("WindowConfig")
63 .field("size", &self.size)
64 .field("min_size", &self.min_size)
65 .field("max_size", &self.max_size)
66 .field("decorations", &self.decorations)
67 .field("title", &self.title)
68 .field("transparent", &self.transparent)
69 .field("background", &self.background)
70 .field("resizable", &self.resizable)
71 .field("icon", &self.icon)
72 .finish()
73 }
74}
75
76impl WindowConfig {
77 pub fn new(app: impl Into<FpRender>) -> Self {
79 Self::new_with_defaults(app.into())
80 }
81
82 fn new_with_defaults(app: impl Into<FpRender>) -> Self {
83 Self {
84 app: app.into(),
85 size: (700.0, 500.0),
86 min_size: None,
87 max_size: None,
88 decorations: true,
89 title: "Freya",
90 transparent: false,
91 background: Color::WHITE,
92 resizable: true,
93 icon: None,
94 window_attributes_hook: None,
95 window_handle_hook: None,
96 }
97 }
98
99 pub fn with_size(mut self, width: f64, height: f64) -> Self {
101 self.size = (width, height);
102 self
103 }
104
105 pub fn with_min_size(mut self, min_width: f64, min_height: f64) -> Self {
107 self.min_size = Some((min_width, min_height));
108 self
109 }
110
111 pub fn with_max_size(mut self, max_width: f64, max_height: f64) -> Self {
113 self.max_size = Some((max_width, max_height));
114 self
115 }
116
117 pub fn with_decorations(mut self, decorations: bool) -> Self {
119 self.decorations = decorations;
120 self
121 }
122
123 pub fn with_title(mut self, title: &'static str) -> Self {
125 self.title = title;
126 self
127 }
128
129 pub fn with_transparency(mut self, transparency: bool) -> Self {
131 self.transparent = transparency;
132 self
133 }
134
135 pub fn with_background(mut self, background: impl Into<Color>) -> Self {
137 self.background = background.into();
138 self
139 }
140
141 pub fn with_resizable(mut self, resizable: bool) -> Self {
143 self.resizable = resizable;
144 self
145 }
146
147 pub fn with_icon(mut self, icon: Icon) -> Self {
149 self.icon = Some(icon);
150 self
151 }
152
153 pub fn with_window_attributes(
155 mut self,
156 window_attributes_hook: impl FnOnce(WindowAttributes, &ActiveEventLoop) -> WindowAttributes
157 + 'static
158 + Send
159 + Sync,
160 ) -> Self {
161 self.window_attributes_hook = Some(Box::new(window_attributes_hook));
162 self
163 }
164
165 pub fn with_window_handle(
167 mut self,
168 window_handle_hook: impl FnOnce(&mut Window) + 'static + Send + Sync,
169 ) -> Self {
170 self.window_handle_hook = Some(Box::new(window_handle_hook));
171 self
172 }
173}
174
175pub type EmbeddedFonts = Vec<(Cow<'static, str>, Bytes)>;
176#[cfg(feature = "tray")]
177pub type TrayIconGetter = Box<dyn FnOnce() -> tray_icon::TrayIcon + Send>;
178#[cfg(feature = "tray")]
179pub type TrayHandler = Box<dyn FnMut(crate::tray_icon::TrayEvent, crate::tray_icon::TrayContext)>;
180
181pub struct LaunchConfig {
183 pub(crate) windows_configs: Vec<WindowConfig>,
184 #[cfg(feature = "tray")]
185 pub(crate) tray: (Option<TrayIconGetter>, Option<TrayHandler>),
186 pub(crate) plugins: PluginsManager,
187 pub(crate) embedded_fonts: EmbeddedFonts,
188 pub(crate) fallback_fonts: Vec<Cow<'static, str>>,
189 pub(crate) futures: Vec<Pin<Box<dyn Future<Output = ()>>>>,
190}
191
192impl Default for LaunchConfig {
193 fn default() -> Self {
194 LaunchConfig {
195 windows_configs: Vec::default(),
196 #[cfg(feature = "tray")]
197 tray: (None, None),
198 plugins: PluginsManager::default(),
199 embedded_fonts: Default::default(),
200 fallback_fonts: default_fonts(),
201 futures: Vec::new(),
202 }
203 }
204}
205
206impl LaunchConfig {
207 pub fn new() -> LaunchConfig {
208 LaunchConfig::default()
209 }
210
211 pub fn window_icon(icon: &[u8]) -> Icon {
212 let reader = ImageReader::new(Cursor::new(icon))
213 .with_guessed_format()
214 .expect("Cursor io never fails");
215 let image = reader
216 .decode()
217 .expect("Failed to open icon path")
218 .into_rgba8();
219 let (width, height) = image.dimensions();
220 let rgba = image.into_raw();
221 Icon::from_rgba(rgba, width, height).expect("Failed to open icon")
222 }
223
224 #[cfg(feature = "tray")]
225 pub fn tray_icon(icon: &[u8]) -> tray_icon::Icon {
226 let reader = ImageReader::new(Cursor::new(icon))
227 .with_guessed_format()
228 .expect("Cursor io never fails");
229 let image = reader
230 .decode()
231 .expect("Failed to open icon path")
232 .into_rgba8();
233 let (width, height) = image.dimensions();
234 let rgba = image.into_raw();
235 tray_icon::Icon::from_rgba(rgba, width, height).expect("Failed to open icon")
236 }
237}
238
239impl LaunchConfig {
240 pub fn with_window(mut self, window_config: WindowConfig) -> Self {
242 self.windows_configs.push(window_config);
243 self
244 }
245
246 #[cfg(feature = "tray")]
248 pub fn with_tray(
249 mut self,
250 tray_icon: impl FnOnce() -> tray_icon::TrayIcon + 'static + Send,
251 tray_handler: impl FnMut(crate::tray_icon::TrayEvent, crate::tray_icon::TrayContext) + 'static,
252 ) -> Self {
253 self.tray = (Some(Box::new(tray_icon)), Some(Box::new(tray_handler)));
254 self
255 }
256
257 pub fn with_plugin(mut self, plugin: impl FreyaPlugin + 'static) -> Self {
259 self.plugins.add_plugin(plugin);
260 self
261 }
262
263 pub fn with_font(
265 mut self,
266 font_name: impl Into<Cow<'static, str>>,
267 font: impl Into<Bytes>,
268 ) -> Self {
269 self.embedded_fonts.push((font_name.into(), font.into()));
270 self
271 }
272
273 pub fn with_fallback_font(mut self, font_family: impl Into<Cow<'static, str>>) -> Self {
275 self.fallback_fonts.push(font_family.into());
276 self
277 }
278
279 pub fn with_default_font(mut self, font_name: impl Into<Cow<'static, str>>) -> Self {
281 self.fallback_fonts.insert(0, font_name.into());
282 self
283 }
284
285 pub fn with_future(mut self, future: impl Future<Output = ()> + 'static) -> Self {
287 self.futures.push(Box::pin(future));
288 self
289 }
290}