maycoon_core/app/
runner.rs

1use crate::app::context::AppContext;
2use crate::app::font_ctx::FontContext;
3use crate::app::handler::AppHandler;
4use crate::app::update::UpdateManager;
5use crate::config::MayConfig;
6use crate::plugin::PluginManager;
7use crate::widget::Widget;
8use maycoon_theme::theme::Theme;
9use peniko::Font;
10use winit::dpi::{LogicalPosition, LogicalSize, Position, Size};
11use winit::event_loop::EventLoopBuilder;
12use winit::window::WindowAttributes;
13
14/// The core Application structure.
15pub struct MayRunner<T: Theme> {
16    config: MayConfig<T>,
17    font_ctx: FontContext,
18}
19
20impl<T: Theme> MayRunner<T> {
21    /// Create a new App with the given [MayConfig].
22    pub fn new(config: MayConfig<T>) -> Self {
23        // init task runner
24        if let Some(config) = &config.tasks {
25            log::info!("Initializing task runner.");
26
27            crate::tasks::runner::TaskRunner::new(config.stack_size, config.workers)
28                .expect("Failed to create task runner")
29                .init()
30                .expect("Failed to init task runner");
31        }
32
33        Self {
34            config,
35            font_ctx: FontContext::default(),
36        }
37    }
38
39    /// Insert a new font into the font context.
40    pub fn with_font(mut self, name: impl ToString, font: Font) -> Self {
41        self.font_ctx.insert(name, font);
42        self
43    }
44
45    /// Insert a new system font into the font context.
46    pub fn with_system_font(mut self, name: impl ToString, postscript_name: impl ToString) -> Self {
47        self.font_ctx.insert_system_font(name, postscript_name);
48        self
49    }
50
51    /// Set the font context. Can be used to configure fonts.
52    pub fn with_font_context(mut self, font_ctx: FontContext) -> Self {
53        self.font_ctx = font_ctx;
54        self
55    }
56
57    /// Run the application with given widget and state.
58    pub fn run<W, F>(mut self, builder: F, mut plugins: PluginManager<T>)
59    where
60        W: Widget,
61        F: FnOnce(AppContext) -> W,
62    {
63        let mut event_loop = EventLoopBuilder::default()
64            .build()
65            .expect("Failed to create event loop");
66
67        let mut attrs = WindowAttributes::default()
68            .with_inner_size(LogicalSize::new(
69                self.config.window.size.x,
70                self.config.window.size.y,
71            ))
72            .with_resizable(self.config.window.resizable)
73            .with_enabled_buttons(self.config.window.buttons)
74            .with_title(self.config.window.title.clone())
75            .with_maximized(self.config.window.maximized)
76            .with_visible(self.config.window.visible)
77            .with_transparent(self.config.window.transparent)
78            .with_blur(self.config.window.blur)
79            .with_decorations(self.config.window.decorations)
80            .with_window_icon(self.config.window.icon.clone())
81            .with_content_protected(self.config.window.content_protected)
82            .with_window_level(self.config.window.level)
83            .with_active(self.config.window.active)
84            .with_cursor(self.config.window.cursor.clone());
85
86        // since `with_max_inner_size()` doesn't support `Option` values, we need to manually set it
87        attrs.max_inner_size = self
88            .config
89            .window
90            .max_size
91            .map(|v| Size::Logical(LogicalSize::new(v.x, v.y)));
92
93        // since `with_min_inner_size()` doesn't support `Option` values, we need to manually set it
94        attrs.min_inner_size = self
95            .config
96            .window
97            .min_size
98            .map(|v| Size::Logical(LogicalSize::new(v.x, v.y)));
99
100        // since `with_position()` doesn't support `Option` values, we need to manually set it
101        attrs.position = self
102            .config
103            .window
104            .position
105            .map(|v| Position::Logical(LogicalPosition::new(v.x, v.y)));
106
107        // since `with_resize_increments()` doesn't support `Option` values, we need to manually set it
108        attrs.resize_increments = self
109            .config
110            .window
111            .resize_increments
112            .map(|v| Size::Logical(LogicalSize::new(v.x, v.y)));
113
114        log::info!("Launching Application...");
115
116        let update = UpdateManager::new();
117
118        let widget = builder(AppContext::new(update.clone()));
119
120        plugins.run(|pl| pl.init(&mut event_loop, &update, &mut attrs, &mut self.config));
121
122        event_loop
123            .run_app(&mut AppHandler::new(
124                attrs,
125                self.config,
126                widget,
127                self.font_ctx,
128                update,
129                plugins,
130            ))
131            .expect("Failed to run event loop");
132    }
133}