pixels_graphics_lib/
lib.rs

1//! Rust Graphics Lib
2//!
3//! This is a simple pixel graphics and GUI library, it provides basic shape, image and text rendering.
4//!
5//! This boilerplate code is needed to use it:
6//!
7//! ```
8//! # use std::error::Error;
9//! # use pixels_graphics_lib::prelude::*;
10//! # use buffer_graphics_lib::Graphics;
11//! # use simple_game_utils::prelude::Timing;
12//!
13//! struct Example {
14//! }
15//!
16//! impl System for Example {
17//!   fn update(&mut self, timing: &Timing, _: &Window) {
18//!
19//!   }
20//!
21//!   fn render(&mut self, graphics: &mut Graphics) {
22//!
23//!   }
24//! }
25//!
26//! fn main() -> Result<(), Box<dyn Error>> {
27//! let system = Example {};
28//!   run(240, 160, "Doc Example", Box::new(system), Options::default())?;
29//!   Ok(())
30//! }
31//!```
32
33#[cfg(all(not(feature = "pixels"), not(feature = "softbuffer"),))]
34compile_error!("You must pick one windowing feature either pixels or softbuffer");
35
36pub mod dialogs;
37mod integration;
38#[cfg(feature = "scenes")]
39pub mod scenes;
40pub mod ui;
41pub mod utilities;
42#[cfg(feature = "window_prefs")]
43pub mod window_prefs;
44
45use crate::prelude::{winit, Coord, ALL_KEYS};
46use crate::ui::styles::UiStyle;
47#[cfg(feature = "window_prefs")]
48use crate::window_prefs::WindowPreferences;
49pub use buffer_graphics_lib;
50use buffer_graphics_lib::Graphics;
51use rustc_hash::FxHashMap;
52#[cfg(feature = "serde")]
53use serde::{Deserialize, Serialize};
54use simple_game_utils::prelude::*;
55use thiserror::Error;
56use winit::event::MouseButton;
57#[cfg(feature = "softbuffer")]
58pub use winit::event_loop::ControlFlow;
59use winit::keyboard::KeyCode;
60use winit::window::Window;
61
62pub mod prelude {
63    pub use crate::dialogs::*;
64    #[cfg(feature = "pixels")]
65    pub use crate::integration::pixels_winit::run;
66    #[cfg(feature = "softbuffer")]
67    pub use crate::integration::softbuffer_winit::run;
68    #[cfg(feature = "scenes")]
69    pub use crate::scenes::*;
70    pub use crate::utilities::virtual_key_codes::*;
71    #[cfg(feature = "window_prefs")]
72    pub use crate::window_prefs::*;
73    pub use crate::GraphicsError;
74    pub use crate::MouseData;
75    pub use crate::Options;
76    pub use crate::System;
77    pub use crate::WindowScaling;
78    pub use buffer_graphics_lib::prelude::*;
79    pub use rustc_hash::FxHashSet;
80    pub use simple_game_utils::prelude::*;
81    pub use winit::event::MouseButton;
82    pub use winit::keyboard::KeyCode;
83    pub use winit::window::Window;
84    #[cfg(feature = "pixels")]
85    pub use winit_29 as winit;
86    #[cfg(feature = "softbuffer")]
87    pub use winit_30 as winit;
88}
89
90#[derive(Error, Debug)]
91pub enum GraphicsError {
92    #[error("Creating a window: {0}")]
93    WindowInit(String),
94    #[cfg(feature = "pixels")]
95    #[error("Initialising Pixels: {0}")]
96    PixelsInit(#[source] pixels::Error),
97    #[error("Saving window pref: {0}")]
98    SavingWindowPref(String),
99    #[cfg(feature = "window_prefs")]
100    #[error("Loading window pref: {0}")]
101    LoadingWindowPref(String),
102    #[error("Invalid pixel array length, expected: {0}, found: {1}")]
103    ImageInitSize(usize, usize),
104    #[error("Both images must be the same size, expected: {0}x{1}, found: {2}x{3}")]
105    ImageBlendSize(usize, usize, usize, usize),
106    #[cfg(feature = "controller")]
107    #[error("Unable to init controller: {0}")]
108    ControllerInit(String),
109    #[cfg(any(feature = "pixels", feature = "softbuffer"))]
110    #[error("Initialing Winit: {0}")]
111    WinitInit(#[source] winit::error::EventLoopError),
112}
113
114#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
115#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
116pub enum WindowScaling {
117    /// Use system DPI
118    Native,
119    /// Use system DPI + 2
120    Double,
121    /// Use system DPI + 4
122    Quad,
123}
124
125#[allow(unused_variables)]
126pub trait System {
127    /// List of keys that your app uses
128    fn keys_used(&self) -> &[KeyCode] {
129        &ALL_KEYS
130    }
131    #[cfg(feature = "window_prefs")]
132    fn window_prefs(&mut self) -> Option<WindowPreferences> {
133        None
134    }
135    fn update(&mut self, timing: &Timing, window: &Window);
136    fn render(&mut self, graphics: &mut Graphics);
137    fn on_mouse_move(&mut self, mouse: &MouseData) {}
138    fn on_mouse_down(&mut self, mouse: &MouseData, button: MouseButton) {}
139    fn on_mouse_up(&mut self, mouse: &MouseData, button: MouseButton) {}
140    fn on_scroll(&mut self, mouse: &MouseData, x_diff: isize, y_diff: isize) {}
141    fn on_key_down(&mut self, keys: Vec<KeyCode>) {}
142    fn on_key_up(&mut self, keys: Vec<KeyCode>) {}
143    fn on_window_closed(&mut self) {}
144    fn on_visibility_changed(&mut self, visible: bool) {}
145    fn on_focus_changed(&mut self, focused: bool) {}
146    fn should_exit(&mut self) -> bool {
147        false
148    }
149}
150
151/// Options for program windows
152#[cfg_attr(feature = "pixels_serde", derive(Serialize, Deserialize))]
153#[cfg_attr(feature = "softbuffer_serde", derive(Serialize, Deserialize))]
154#[derive(Debug, PartialEq)]
155pub struct Options {
156    /// Target and max number of times [Scene::update] can be called per second
157    /// Default is 240
158    pub ups: usize,
159    /// How the window should be scaled
160    /// Default is [Auto][WindowScaling::Auto]
161    pub scaling: WindowScaling,
162    /// If vsync should be enabled
163    /// Default is true
164    pub vsync: bool,
165    /// If OS mouse cursor should be hidden
166    /// (you'll have to draw your own if this is true, this is often called software cursor in games)
167    /// Default is false
168    pub hide_cursor: bool,
169    /// If the mouse cursor should be locked to within this window while it's in the foreground
170    /// Default is false
171    pub confine_cursor: bool,
172    /// Style data for [UiElement]
173    pub style: UiStyle,
174    /// Control how the program loops, see [Winit ControlFlow](https://docs.rs/winit/latest/winit/event_loop/enum.ControlFlow.html)
175    #[cfg(feature = "softbuffer")]
176    pub control_flow: ControlFlow,
177}
178
179impl Options {
180    pub fn new(
181        ups: usize,
182        scaling: WindowScaling,
183        vsync: bool,
184        hide_cursor: bool,
185        confine_cursor: bool,
186        style: UiStyle,
187        #[cfg(feature = "softbuffer")] control_flow: ControlFlow,
188    ) -> Self {
189        Self {
190            ups,
191            scaling,
192            vsync,
193            hide_cursor,
194            confine_cursor,
195            style,
196            #[cfg(feature = "softbuffer")]
197            control_flow,
198        }
199    }
200}
201
202impl Default for Options {
203    fn default() -> Self {
204        Self {
205            ups: 240,
206            scaling: WindowScaling::Double,
207            vsync: true,
208            hide_cursor: false,
209            confine_cursor: false,
210            style: UiStyle::default(),
211            #[cfg(feature = "softbuffer")]
212            control_flow: ControlFlow::Poll,
213        }
214    }
215}
216
217#[cfg_attr(feature = "pixels_serde", derive(Serialize, Deserialize))]
218#[cfg_attr(feature = "softbuffer_serde", derive(Serialize, Deserialize))]
219#[derive(Debug, Clone, Eq, PartialEq, Default)]
220pub struct MouseData {
221    pub xy: Coord,
222    buttons: FxHashMap<MouseButton, Coord>,
223}
224
225impl MouseData {
226    pub fn any_held(&self) -> bool {
227        !self.buttons.is_empty()
228    }
229
230    /// Returns the press location if the mouse button is currently held down
231    pub fn is_down(&self, button: MouseButton) -> Option<Coord> {
232        self.buttons.get(&button).cloned()
233    }
234
235    pub(crate) fn add_up(&mut self, button: MouseButton) {
236        self.buttons.remove(&button);
237    }
238
239    pub(crate) fn add_down(&mut self, xy: Coord, button: MouseButton) {
240        self.buttons.insert(button, xy);
241    }
242}