swamp_window/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
/*
 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/piot/swamp-window
 * Licensed under the MIT License. See LICENSE in the project root for license information.
 */

use crate::dpi::LogicalSize;
use crate::dpi::PhysicalSize;
use log::info;
use std::sync::Arc;
use winit::application::ApplicationHandler;
use winit::dpi;
use winit::error::EventLoopError;
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
use winit::window::{Window, WindowAttributes, WindowId};

/// A trait for handling application-specific window creation and management.
///
/// The `AppHandler` trait defines the behavior required for handling
/// the creation of application windows. Implementing this trait allows
/// for customized window management tailored to the needs of your application.
pub trait AppHandler {
    fn create_window(&mut self, window: Arc<Window>);

    fn resized(&mut self, size: dpi::PhysicalSize<u32>);

    fn min_size(&self) -> (u16, u16);

    fn redraw(&mut self);
}

pub struct App<'a> {
    window: Option<Arc<Window>>,
    handler: &'a mut (dyn AppHandler),
    window_attributes: WindowAttributes,
    next_resize: Option<PhysicalSize<u32>>,
}

impl<'a> App<'a> {
    pub fn new(
        handler: &'a mut dyn AppHandler,
        title: &str,
        min_width: u16,
        min_height: u16,
    ) -> Self {
        let min_size = LogicalSize::new(min_width as f64, min_height as f64);
        let window_attributes = WindowAttributes::default()
            .with_title(title)
            .with_resizable(true)
            .with_min_inner_size(min_size);
        Self {
            handler,
            window: None,
            window_attributes,
            next_resize: None,
        }
    }
}

impl ApplicationHandler for App<'_> {
    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
        if self.window.is_none() {
            info!("creating new window");

            let window = Arc::new(
                event_loop
                    .create_window(self.window_attributes.clone())
                    .unwrap(),
            );
            self.window = Some(window.clone());

            self.handler.create_window(window);
            info!("created the window");
        }
    }

    fn window_event(&mut self, event_loop: &ActiveEventLoop, id: WindowId, event: WindowEvent) {
        if id != self.window.as_ref().unwrap().id() {
            return;
        }

        match event {
            WindowEvent::CloseRequested => {
                event_loop.exit();
            }
            WindowEvent::Resized(physical_size) => {
                self.next_resize = Some(physical_size);

                // This tells winit that we want another frame after this one
                self.window.as_ref().unwrap().request_redraw();
            }
            WindowEvent::RedrawRequested => {
                if let Some(next_resize) = self.next_resize {
                    self.handler.resized(next_resize);
                    self.next_resize = None;
                }
                // This tells winit that we want another frame after this one
                self.window.as_ref().unwrap().request_redraw();

                if self.window.is_some() {
                    self.handler.redraw();
                }
            }
            _ => {}
        }
    }

    fn suspended(&mut self, _: &ActiveEventLoop) {}

    fn exiting(&mut self, _: &ActiveEventLoop) {}
}

/// A struct responsible for managing the application window lifecycle.
///
/// The `WindowRunner` struct provides functionality to run an application
/// that utilizes an event loop for window management. It abstracts the details
/// of creating and running the event loop, making it easier to integrate window
/// handling into your game application.
pub struct WindowRunner;

impl WindowRunner {
    /// Runs the application with the provided handler.
    ///
    /// This method initializes an event loop and starts the application by
    /// executing the provided `AppHandler`. The event loop runs in a polling
    /// mode, allowing for responsive event handling. It is not guaranteed to ever return.
    ///
    /// # Parameters
    ///
    /// - `handler`: A mutable reference to an object implementing the `AppHandler`
    ///   trait, which defines the behavior of the application in response to events.
    ///
    /// # Returns
    ///
    /// This method returns a `Result<(), EventLoopError>`.
    /// If an error occurs during event loop creation, it returns an `EventLoopError`.
    ///
    /// # Note
    ///
    /// It is not guaranteed to ever return, as the event loop will run indefinitely
    /// until the application is terminated.
    ///
    /// # Example
    ///
    /// ```rust
    /// use swamp_window::WindowRunner;
    /// use async_trait::async_trait;
    /// use swamp_window::AppHandler;
    /// use std::sync::Arc;
    /// use winit::window::Window;
    /// use winit::dpi;
    ///
    /// struct MyApp;
    ///
    /// #[async_trait]
    /// impl AppHandler for MyApp {
    ///     async fn create_window(&mut self, window: Arc<Window>) {
    ///         // Custom window initialization code here
    ///     }
    ///
    /// fn redraw(&mut self) { todo!() }
    /// fn resized(&mut self, size: dpi::PhysicalSize<u32>) { todo!() }
    /// }
    ///
    /// let mut my_app = MyApp;
    ///
    /// #[cfg(test)]
    /// fn test() {
    ///    if let Err(e) = WindowRunner::run_app(&mut my_app) {
    ///       eprintln!("Error running the application: {:?}", e);
    ///    }
    /// }
    /// ```
    pub fn run_app(handler: &mut dyn AppHandler, title: &str) -> Result<(), EventLoopError> {
        let event_loop = EventLoop::new()?;
        event_loop.set_control_flow(ControlFlow::Poll);
        let (min_width, min_height) = handler.min_size();
        let mut app = App::new(handler, title, min_width, min_height);
        let _ = event_loop.run_app(&mut app);
        Ok(())
    }
}