agape/
lib.rs

1//! Agape is a cross-platform GUI library.
2//!
3//! ## Overview
4//! At the core of `agape` is widgets, a [`Widget`] is anything that holds state and can be drawn
5//! to the screen. To get started create an [`App`] with a root widget.
6//!
7//! ```no_run
8//! use agape::{App,widgets::*,Error};
9//! fn main() -> Result<(),Error>{
10//!     App::new(Home())
11//!         .run()
12//! }
13//!
14//! fn Home() -> impl Widget{
15//!     Text::new("Hello!!")
16//!         .font_size(24)
17//! }
18//! ```
19mod assets;
20pub mod error;
21mod macros;
22pub mod message;
23pub mod resources;
24mod state;
25pub mod style;
26pub mod widgets;
27
28pub use agape_core::*;
29pub use agape_layout as layout;
30pub use agape_macros::hex;
31pub use agape_renderer as renderer;
32pub use error::{Error, Result};
33pub use message::{Message, MessageQueue};
34use std::path::Path;
35
36use crate::message::{MouseButtonDown, MouseButtonUp};
37use crate::state::{Scroll, State};
38use crate::widgets::Widget;
39pub use agape_macros::Widget;
40use pixels::{Pixels, SurfaceTexture};
41use std::sync::Arc;
42use tracing::info;
43use winit::event::{ElementState, MouseButton, MouseScrollDelta};
44use winit::event_loop::ActiveEventLoop;
45use winit::{
46    application::ApplicationHandler,
47    event::WindowEvent,
48    event_loop::{ControlFlow, EventLoop},
49    window::Window,
50    window::WindowId,
51};
52
53/// An `App` is a single program.
54pub struct App<'app> {
55    // The window and pixel buffer only get populated
56    // when the window actually opens.
57    window: Option<Arc<Window>>,
58    pixels: Option<Pixels<'app>>,
59    state: State,
60}
61
62impl App<'_> {
63    /// Create a new app.
64    pub fn new(widget: impl Widget + 'static) -> Self {
65        Self {
66            state: State::new(widget),
67            pixels: None,
68            window: None,
69        }
70    }
71
72    pub fn assets(mut self, path: impl AsRef<Path>) -> Self {
73        self.state.asset_dir(path);
74        self
75    }
76
77    fn render(&mut self) {
78        self.state.render();
79        let renderer = self.state.renderer();
80        let pixels = self.pixels.as_mut().unwrap();
81
82        pixels.frame_mut().copy_from_slice(renderer.pixmap().data());
83        pixels.render().unwrap();
84    }
85
86    /// Run the app.
87    ///
88    /// # Panics
89    /// The app will panic if it is run in another thread, this is
90    /// because accessing windows in other threads is unsafe on
91    /// certain platforms.
92    pub fn run(mut self) -> Result<()> {
93        let event_loop = EventLoop::new()?;
94        event_loop.set_control_flow(ControlFlow::Poll);
95        event_loop.run_app(&mut self)?;
96        Ok(())
97    }
98}
99
100impl ApplicationHandler for App<'_> {
101    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
102        info!("Initializing resources");
103        let window = event_loop.create_window(Default::default()).unwrap();
104        let window = Arc::new(window);
105
106        let size = Size::from(window.inner_size());
107        let width = size.width as u32;
108        let height = size.height as u32;
109
110        let surface = SurfaceTexture::new(width, height, Arc::clone(&window));
111        let pixels = Pixels::new(width, height, surface).unwrap();
112
113        self.pixels = Some(pixels);
114        self.window = Some(Arc::clone(&window));
115    }
116
117    fn window_event(&mut self, event_loop: &ActiveEventLoop, _: WindowId, event: WindowEvent) {
118        match event {
119            WindowEvent::CloseRequested => {
120                info!("Exiting app");
121                event_loop.exit();
122            }
123            WindowEvent::CursorMoved { position, .. } => {
124                self.state.update_cursor_position(position.into());
125            }
126            WindowEvent::MouseInput { state, button, .. } => {
127                let messages = self.state.messages_mut();
128                if let MouseButton::Left = button
129                    && let ElementState::Pressed = state
130                {
131                    messages.add(MouseButtonDown);
132                }
133
134                if let MouseButton::Left = button
135                    && let ElementState::Released = state
136                {
137                    messages.add(MouseButtonUp);
138                }
139            }
140            WindowEvent::RedrawRequested => {
141                self.render();
142                self.window.as_mut().unwrap().request_redraw();
143            }
144            WindowEvent::Resized(size) => {
145                self.pixels
146                    .as_mut()
147                    .unwrap()
148                    .resize_surface(size.width, size.height)
149                    .expect("Failed to resize the pixel buffer");
150
151                self.pixels
152                    .as_mut()
153                    .unwrap()
154                    .resize_buffer(size.width, size.height)
155                    .expect("Failed to resize the pixel buffer");
156
157                self.state.resize(size.into());
158            }
159            WindowEvent::KeyboardInput { event, .. } => {
160                self.state.key_event(&event);
161            }
162            WindowEvent::MouseWheel {
163                delta: MouseScrollDelta::LineDelta(_, y),
164                ..
165            } => {
166                self.state.messages_mut().add(Scroll(y));
167            }
168            _ => {}
169        }
170        self.state.update();
171    }
172}