freya_winit/
config.rs

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
28/// Configuration for a Window.
29pub struct WindowConfig {
30    /// Root component for the window app.
31    pub(crate) app: FpRender,
32    /// Size of the Window.
33    pub(crate) size: (f64, f64),
34    /// Minimum size of the Window.
35    pub(crate) min_size: Option<(f64, f64)>,
36    /// Maximum size of the Window.
37    pub(crate) max_size: Option<(f64, f64)>,
38    /// Enable Window decorations.
39    pub(crate) decorations: bool,
40    /// Title for the Window.
41    pub(crate) title: &'static str,
42    /// Make the Window transparent or not.
43    pub(crate) transparent: bool,
44    /// Background color of the Window.
45    pub(crate) background: Color,
46    /// Enable Window resizable behaviour.
47    pub(crate) resizable: bool,
48    /// Icon for the Window.
49    pub(crate) icon: Option<Icon>,
50    /// Hook function called with the Window Attributes.
51    pub(crate) window_attributes_hook: Option<WindowBuilderHook>,
52    /// Hook function called with the Window.
53    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    /// Create a window with the given app.
74    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    /// Specify a Window size.
96    pub fn with_size(mut self, width: f64, height: f64) -> Self {
97        self.size = (width, height);
98        self
99    }
100
101    /// Specify a minimum Window size.
102    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    /// Specify a maximum Window size.
108    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    /// Whether the Window will have decorations or not.
114    pub fn with_decorations(mut self, decorations: bool) -> Self {
115        self.decorations = decorations;
116        self
117    }
118
119    /// Specify the Window title.
120    pub fn with_title(mut self, title: &'static str) -> Self {
121        self.title = title;
122        self
123    }
124
125    /// Make the Window transparent or not.
126    pub fn with_transparency(mut self, transparency: bool) -> Self {
127        self.transparent = transparency;
128        self
129    }
130
131    /// Specify the Window's background color.
132    pub fn with_background(mut self, background: impl Into<Color>) -> Self {
133        self.background = background.into();
134        self
135    }
136
137    /// Is Window resizable.
138    pub fn with_resizable(mut self, resizable: bool) -> Self {
139        self.resizable = resizable;
140        self
141    }
142
143    /// Specify Window icon.
144    pub fn with_icon(mut self, icon: Icon) -> Self {
145        self.icon = Some(icon);
146        self
147    }
148
149    /// Register a Window Attributes hook.
150    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    /// Register a Window handle hook.
162    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
177/// Launch configuration.
178pub 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    /// Register a window configuration. You can call this multiple times.
237    pub fn with_window(mut self, window_config: WindowConfig) -> Self {
238        self.windows_configs.push(window_config);
239        self
240    }
241
242    /// Register a tray icon and its handler.
243    #[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    /// Register a plugin.
254    pub fn with_plugin(mut self, plugin: impl FreyaPlugin + 'static) -> Self {
255        self.plugins.add_plugin(plugin);
256        self
257    }
258
259    /// Embed a font.
260    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    /// Register a fallback font. Will be used if the default fonts are not available.
270    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    /// Register a default font. Will be used if found.
276    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    /// Register a single-thread future / async task.
282    pub fn with_future(mut self, future: impl Future<Output = ()> + 'static) -> Self {
283        self.futures.push(Box::pin(future));
284        self
285    }
286}