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::{
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
32/// Configuration for a Window.
33pub struct WindowConfig {
34    /// Root component for the window app.
35    pub(crate) app: FpRender,
36    /// Size of the Window.
37    pub(crate) size: (f64, f64),
38    /// Minimum size of the Window.
39    pub(crate) min_size: Option<(f64, f64)>,
40    /// Maximum size of the Window.
41    pub(crate) max_size: Option<(f64, f64)>,
42    /// Enable Window decorations.
43    pub(crate) decorations: bool,
44    /// Title for the Window.
45    pub(crate) title: &'static str,
46    /// Make the Window transparent or not.
47    pub(crate) transparent: bool,
48    /// Background color of the Window.
49    pub(crate) background: Color,
50    /// Enable Window resizable behaviour.
51    pub(crate) resizable: bool,
52    /// Icon for the Window.
53    pub(crate) icon: Option<Icon>,
54    /// Hook function called with the Window Attributes.
55    pub(crate) window_attributes_hook: Option<WindowBuilderHook>,
56    /// Hook function called with the Window.
57    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    /// Create a window with the given app.
78    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    /// Specify a Window size.
100    pub fn with_size(mut self, width: f64, height: f64) -> Self {
101        self.size = (width, height);
102        self
103    }
104
105    /// Specify a minimum Window size.
106    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    /// Specify a maximum Window size.
112    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    /// Whether the Window will have decorations or not.
118    pub fn with_decorations(mut self, decorations: bool) -> Self {
119        self.decorations = decorations;
120        self
121    }
122
123    /// Specify the Window title.
124    pub fn with_title(mut self, title: &'static str) -> Self {
125        self.title = title;
126        self
127    }
128
129    /// Make the Window transparent or not.
130    pub fn with_transparency(mut self, transparency: bool) -> Self {
131        self.transparent = transparency;
132        self
133    }
134
135    /// Specify the Window's background color.
136    pub fn with_background(mut self, background: impl Into<Color>) -> Self {
137        self.background = background.into();
138        self
139    }
140
141    /// Is Window resizable.
142    pub fn with_resizable(mut self, resizable: bool) -> Self {
143        self.resizable = resizable;
144        self
145    }
146
147    /// Specify Window icon.
148    pub fn with_icon(mut self, icon: Icon) -> Self {
149        self.icon = Some(icon);
150        self
151    }
152
153    /// Register a Window Attributes hook.
154    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    /// Register a Window handle hook.
166    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
181/// Launch configuration.
182pub 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    /// Register a window configuration. You can call this multiple times.
241    pub fn with_window(mut self, window_config: WindowConfig) -> Self {
242        self.windows_configs.push(window_config);
243        self
244    }
245
246    /// Register a tray icon and its handler.
247    #[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    /// Register a plugin.
258    pub fn with_plugin(mut self, plugin: impl FreyaPlugin + 'static) -> Self {
259        self.plugins.add_plugin(plugin);
260        self
261    }
262
263    /// Embed a font.
264    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    /// Register a fallback font. Will be used if the default fonts are not available.
274    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    /// Register a default font. Will be used if found.
280    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    /// Register a single-thread future / async task.
286    pub fn with_future(mut self, future: impl Future<Output = ()> + 'static) -> Self {
287        self.futures.push(Box::pin(future));
288        self
289    }
290}