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