diff options
| author | Arne Dußin | 2020-11-09 21:15:35 +0100 |
|---|---|---|
| committer | Arne Dußin | 2020-11-09 21:15:35 +0100 |
| commit | b06e0075bf4dfd51f8ad5df801f9c43fbd73df1f (patch) | |
| tree | bb9de75363ade9f2f27ebdb60507dbefb36afc35 | |
| parent | e08cb85528e6b72d5f3b2fbeb79b581fa7a4e461 (diff) | |
| download | graf_karto-b06e0075bf4dfd51f8ad5df801f9c43fbd73df1f.tar.gz graf_karto-b06e0075bf4dfd51f8ad5df801f9c43fbd73df1f.zip | |
Add configuration options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | src/button.rs | 176 | ||||
| -rw-r--r-- | src/config.rs | 136 | ||||
| -rw-r--r-- | src/editor.rs | 11 | ||||
| -rw-r--r-- | src/main.rs | 28 | ||||
| -rw-r--r-- | src/tool/deletion_tool.rs | 29 | ||||
| -rw-r--r-- | src/tool/icon_tool.rs | 22 | ||||
| -rw-r--r-- | src/tool/mod.rs | 3 | ||||
| -rw-r--r-- | src/tool/room_tool.rs | 27 | ||||
| -rw-r--r-- | src/tool/wall_tool.rs | 27 |
10 files changed, 414 insertions, 46 deletions
@@ -1,2 +1,3 @@ /target swap.ron +config.ron diff --git a/src/button.rs b/src/button.rs new file mode 100644 index 0000000..ae78dbb --- /dev/null +++ b/src/button.rs @@ -0,0 +1,176 @@ +//! Reimplementation crate of the KeyboardKey and MouseButton structs of raylib, because they do +//! not implement `serde::Serialize` and `serde::Deserialize`. If you have a better idea on how to +//! handle it, feel free. + +use raylib::ffi::{KeyboardKey as rlKeyboardKey, MouseButton as rlMouseButton}; +use raylib::RaylibHandle; +use serde::{Deserialize, Serialize}; +use std::mem; + +/// Enum to abstract the distinction of keyboard keys and mouse keys that raylib does away so the +/// user has free reign over what key they use for what purpose. +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum Button { + Mouse(MouseButton), + Keyboard(KeyboardKey), +} + +#[repr(u32)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum MouseButton { + Left = 0, + Right = 1, + Middle = 2, +} + +#[repr(u32)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum KeyboardKey { + Apostrophe = 39, + Comma = 44, + Minus = 45, + Period = 46, + Slash = 47, + Zero = 48, + One = 49, + Two = 50, + Three = 51, + Four = 52, + Five = 53, + Six = 54, + Seven = 55, + Eight = 56, + Nine = 57, + Semicolon = 59, + Equal = 61, + A = 65, + B = 66, + C = 67, + D = 68, + E = 69, + F = 70, + G = 71, + H = 72, + I = 73, + J = 74, + K = 75, + L = 76, + M = 77, + N = 78, + O = 79, + P = 80, + Q = 81, + R = 82, + S = 83, + T = 84, + U = 85, + V = 86, + W = 87, + X = 88, + Y = 89, + Z = 90, + Space = 32, + Escape = 256, + Enter = 257, + Tab = 258, + Backspace = 259, + Insert = 260, + Delete = 261, + Right = 262, + Left = 263, + Down = 264, + Up = 265, + PageUp = 266, + PageDown = 267, + Home = 268, + End = 269, + CapsLock = 280, + ScrollLock = 281, + NumLock = 282, + PrintScreen = 283, + Pause = 284, + F1 = 290, + F2 = 291, + F3 = 292, + F4 = 293, + F5 = 294, + F6 = 295, + F7 = 296, + F8 = 297, + F9 = 298, + F10 = 299, + F11 = 300, + F12 = 301, + LeftShift = 340, + LeftControl = 341, + LeftAlt = 342, + LeftSuper = 343, + RightShift = 344, + RightControl = 345, + RightAlt = 346, + RightSuper = 347, + Menu = 348, + LeftBracket = 91, + Backslash = 92, + RightBracket = 93, + Grave = 96, + Keypad0 = 320, + Keypad1 = 321, + Keypad2 = 322, + Keypad3 = 323, + Keypad4 = 324, + Keypad5 = 325, + Keypad6 = 326, + Keypad7 = 327, + Keypad8 = 328, + Keypad9 = 329, + KeypadDecimal = 330, + KeypadDivide = 331, + KeypadMultiply = 332, + KeypadSubtract = 333, + KeypadAdd = 334, + KeypadEnter = 335, + KeypadEqual = 336, +} + +impl Button { + pub fn is_pressed(self, rl: &RaylibHandle) -> bool { + match self { + Self::Mouse(button) => rl.is_mouse_button_pressed(button.into()), + Self::Keyboard(key) => rl.is_key_pressed(key.into()), + } + } +} + +impl From<rlMouseButton> for Button { + fn from(button: rlMouseButton) -> Self { + Self::Mouse(MouseButton::from(button)) + } +} +impl From<rlKeyboardKey> for Button { + fn from(key: rlKeyboardKey) -> Self { + Self::Keyboard(KeyboardKey::from(key)) + } +} + +impl From<rlMouseButton> for MouseButton { + fn from(button: rlMouseButton) -> Self { + unsafe { mem::transmute(button as u32) } + } +} +impl Into<rlMouseButton> for MouseButton { + fn into(self) -> rlMouseButton { + unsafe { mem::transmute(self as u32) } + } +} + +impl From<rlKeyboardKey> for KeyboardKey { + fn from(key: rlKeyboardKey) -> Self { + unsafe { mem::transmute(key as u32) } + } +} +impl Into<rlKeyboardKey> for KeyboardKey { + fn into(self) -> rlKeyboardKey { + unsafe { mem::transmute(self as u32) } + } +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..4e90b21 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,136 @@ +use crate::button::*; +use ron::de::from_reader; +use ron::ser::{to_string_pretty, PrettyConfig}; +use serde::{Deserialize, Serialize}; +use std::fs::File; +use std::io::{self, Write}; +use std::path::Path; + +#[derive(Deserialize, Serialize)] +pub struct Config { + pub deletion_keybindings: DeletionToolKeybindings, + pub icon_keybindings: IconToolKeybindings, + pub room_keybindings: RoomToolKeybindings, + pub wall_keybindings: WallToolKeybindings, +} + +pub trait ToolKeybindings { + /// Get the key which will activate the tool it is made for. + fn activation_key(&self) -> Button; +} + +#[derive(Serialize, Deserialize)] +pub struct DeletionToolKeybindings { + pub activation: Button, + pub start_selection: Button, + pub do_delete: Button, + pub abort_deletion: Button, +} + +#[derive(Serialize, Deserialize)] +pub struct IconToolKeybindings { + pub activation: Button, + pub next: Button, + pub previous: Button, + pub rotate_clockwise: Button, + pub rotate_counterclockwise: Button, + pub place: Button, +} + +#[derive(Serialize, Deserialize)] +pub struct RoomToolKeybindings { + pub activation: Button, + pub start_draw: Button, + pub finish_draw: Button, + pub abort_draw: Button, +} + +#[derive(Serialize, Deserialize)] +pub struct WallToolKeybindings { + pub activation: Button, + pub start_wall: Button, + pub finish_segment: Button, + pub abort_segment: Button, +} + +impl Config { + pub fn from_file<P: AsRef<Path>>(path: P) -> io::Result<Config> { + let file = File::open(&path)?; + match from_reader(file) { + Ok(data) => Ok(data), + Err(err) => Err(io::Error::new(io::ErrorKind::InvalidData, err)), + } + } + + pub fn write_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> { + let mut file = File::create(&path)?; + + let pretty_conf = PrettyConfig::new() + .with_depth_limit(4) + .with_decimal_floats(true) + .with_separate_tuple_members(true) + .with_indentor("\t".to_owned()); + let string = match to_string_pretty(&self, pretty_conf) { + Ok(string) => string, + Err(err) => { + return Err(io::Error::new(io::ErrorKind::InvalidInput, err)); + } + }; + + file.write_all(&string.as_bytes()) + } +} + +impl Default for Config { + fn default() -> Self { + Config { + deletion_keybindings: DeletionToolKeybindings { + activation: Button::Keyboard(KeyboardKey::D), + start_selection: Button::Mouse(MouseButton::Left), + do_delete: Button::Mouse(MouseButton::Left), + abort_deletion: Button::Mouse(MouseButton::Right), + }, + icon_keybindings: IconToolKeybindings { + activation: Button::Keyboard(KeyboardKey::I), + next: Button::Keyboard(KeyboardKey::I), + previous: Button::Keyboard(KeyboardKey::J), + rotate_clockwise: Button::Mouse(MouseButton::Right), + rotate_counterclockwise: Button::Keyboard(KeyboardKey::Minus), + place: Button::Mouse(MouseButton::Left), + }, + room_keybindings: RoomToolKeybindings { + activation: Button::Keyboard(KeyboardKey::R), + start_draw: Button::Mouse(MouseButton::Left), + finish_draw: Button::Mouse(MouseButton::Left), + abort_draw: Button::Mouse(MouseButton::Right), + }, + wall_keybindings: WallToolKeybindings { + activation: Button::Keyboard(KeyboardKey::W), + start_wall: Button::Mouse(MouseButton::Left), + finish_segment: Button::Mouse(MouseButton::Left), + abort_segment: Button::Mouse(MouseButton::Right), + }, + } + } +} + +impl ToolKeybindings for DeletionToolKeybindings { + fn activation_key(&self) -> Button { + self.activation + } +} +impl ToolKeybindings for IconToolKeybindings { + fn activation_key(&self) -> Button { + self.activation + } +} +impl ToolKeybindings for RoomToolKeybindings { + fn activation_key(&self) -> Button { + self.activation + } +} +impl ToolKeybindings for WallToolKeybindings { + fn activation_key(&self) -> Button { + self.activation + } +} diff --git a/src/editor.rs b/src/editor.rs index 01bd268..0999448 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -1,3 +1,4 @@ +use crate::config::Config; use crate::map_data::MapData; use crate::tool::*; use crate::transform::Transform; @@ -12,16 +13,16 @@ pub struct Editor { } impl Editor { - pub fn new(rl: &mut RaylibHandle, rlt: &RaylibThread) -> Self { + pub fn new(rl: &mut RaylibHandle, rlt: &RaylibThread, config: Config) -> Self { let mut tools: Vec<Box<dyn Tool>> = Vec::with_capacity(ToolType::NumTools as usize); assert_eq!(ToolType::RoomTool as u8, 0); - tools.push(Box::new(RoomTool::new())); + tools.push(Box::new(RoomTool::new(config.room_keybindings))); assert_eq!(ToolType::WallTool as u8, 1); - tools.push(Box::new(WallTool::new())); + tools.push(Box::new(WallTool::new(config.wall_keybindings))); assert_eq!(ToolType::IconTool as u8, 2); - tools.push(Box::new(IconTool::new(rl, rlt))); + tools.push(Box::new(IconTool::new(rl, rlt, config.icon_keybindings))); assert_eq!(ToolType::DeletionTool as u8, 3); - tools.push(Box::new(DeletionTool::new())); + tools.push(Box::new(DeletionTool::new(config.deletion_keybindings))); assert_eq!(ToolType::NumTools as usize, tools.len()); diff --git a/src/main.rs b/src/main.rs index a72b172..11cdcfe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +pub mod button; +pub mod config; pub mod editor; pub mod grid; pub mod map_data; @@ -6,15 +8,39 @@ pub mod svg; pub mod tool; pub mod transform; +use config::Config; use editor::Editor; use raylib::prelude::*; +use std::io; use transform::Transform; +pub const CONFIG_FILE: &str = "config.ron"; + fn main() { let (mut rl, thread) = raylib::init().resizable().title("Hello there!").build(); rl.set_target_fps(120); - let mut editor = Editor::new(&mut rl, &thread); + // Load the configuration file, if available. + let config = match Config::from_file(CONFIG_FILE) { + Ok(config) => config, + Err(err) => { + /* Create a default config file if it doesn't exist, otherwise leave the incorrectly + * formatted/corrupted or otherwise unreadable file alone. + */ + let config = Config::default(); + if err.kind() == io::ErrorKind::NotFound { + println!("Could not find a configuration file. Creating default."); + config.write_file(CONFIG_FILE); + } else { + println!("Could not read configuration file: {}", err); + println!("Using defaults for this run."); + } + + config + } + }; + + let mut editor = Editor::new(&mut rl, &thread, config); let mut transform = Transform::new(); let mut last_mouse_pos = rl.get_mouse_position(); diff --git a/src/tool/deletion_tool.rs b/src/tool/deletion_tool.rs index 142e0cb..6f5ac24 100644 --- a/src/tool/deletion_tool.rs +++ b/src/tool/deletion_tool.rs @@ -1,18 +1,22 @@ use super::Tool; +use crate::button::Button; +use crate::config::{DeletionToolKeybindings, ToolKeybindings}; use crate::map_data::MapData; use crate::math::{Rect, Vec2}; use crate::transform::Transform; use raylib::core::drawing::{RaylibDraw, RaylibDrawHandle}; -use raylib::ffi::{Color, MouseButton}; +use raylib::ffi::Color; use raylib::RaylibHandle; pub struct DeletionTool { + keybindings: DeletionToolKeybindings, deletion_rect: Option<(Vec2<f32>, Vec2<f32>)>, } impl DeletionTool { - pub fn new() -> Self { + pub fn new(keybindings: DeletionToolKeybindings) -> Self { Self { + keybindings, deletion_rect: None, } } @@ -38,17 +42,12 @@ impl Tool for DeletionTool { *pos2 = mouse_pos_m; } - if rl.is_mouse_button_pressed(MouseButton::MOUSE_LEFT_BUTTON) { - if let Some((pos1, pos2)) = self.deletion_rect { - Self::delete_rect(map_data, Rect::bounding_rect(pos1, pos2)); - self.deletion_rect = None; - } else { - self.deletion_rect = Some((mouse_pos_m, mouse_pos_m)) - } - } - - // Abort deletion. - if rl.is_mouse_button_pressed(MouseButton::MOUSE_RIGHT_BUTTON) { + if self.keybindings.do_delete.is_pressed(rl) && self.deletion_rect.is_some() { + let (pos1, pos2) = self.deletion_rect.take().unwrap(); + Self::delete_rect(map_data, Rect::bounding_rect(pos1, pos2)); + } else if self.keybindings.start_selection.is_pressed(rl) { + self.deletion_rect = Some((mouse_pos_m, mouse_pos_m)) + } else if self.keybindings.abort_deletion.is_pressed(rl) { self.deletion_rect = None; } } @@ -77,4 +76,8 @@ impl Tool for DeletionTool { ); } } + + fn activation_key(&self) -> Button { + self.keybindings.activation_key() + } } diff --git a/src/tool/icon_tool.rs b/src/tool/icon_tool.rs index 89a07e8..cf90056 100644 --- a/src/tool/icon_tool.rs +++ b/src/tool/icon_tool.rs @@ -1,3 +1,5 @@ +use crate::button::Button; +use crate::config::{IconToolKeybindings, ToolKeybindings}; use crate::grid::snap_to_grid; use crate::map_data::MapData; use crate::math::Vec2; @@ -34,6 +36,7 @@ pub struct IconInfo { pub struct IconTool { // TODO: support svg + keybindings: IconToolKeybindings, /// The icon data, containing the image texture and the info for that image texture like the /// scale the image actually has. icon_data: Vec<(Texture2D, IconFileInfo)>, @@ -43,7 +46,11 @@ pub struct IconTool { } impl IconTool { - pub fn new(rl: &mut RaylibHandle, rlt: &RaylibThread) -> Self { + pub fn new( + rl: &mut RaylibHandle, + rlt: &RaylibThread, + keybindings: IconToolKeybindings, + ) -> Self { /* Read all available icons from the icon directory. SVGs do not need any special scale * file, but pixel-based file formats require a RON-file declaring what the scale of the * picture is right beside them. @@ -87,6 +94,7 @@ impl IconTool { } Self { + keybindings, icon_data, current_icon: None, } @@ -114,18 +122,16 @@ impl Tool for IconTool { { // Unwrap the current icon, since it is now definitely set, as we are in the active update. let active_icon = self.current_icon.as_mut().unwrap(); - // Activate the next icon when pressing the icon tool key. - if rl.is_key_pressed(KeyboardKey::KEY_I) { + if self.keybindings.next.is_pressed(rl) { active_icon.icon_id = (active_icon.icon_id + 1) % self.icon_data.len(); } - if rl.is_mouse_button_pressed(MouseButton::MOUSE_RIGHT_BUTTON) { - // Rotate the currently active icon by a quarter rotation. + if self.keybindings.rotate_clockwise.is_pressed(rl) { active_icon.rotation += 45.; } } // Handle placing the icon on the map - if rl.is_mouse_button_pressed(MouseButton::MOUSE_LEFT_BUTTON) { + if self.keybindings.place.is_pressed(rl) { map.icons_mut().push(self.current_icon.take().unwrap()); } } @@ -175,4 +181,8 @@ impl Tool for IconTool { ); } } + + fn activation_key(&self) -> Button { + self.keybindings.activation_key() + } } diff --git a/src/tool/mod.rs b/src/tool/mod.rs index a3d5964..6b9eba7 100644 --- a/src/tool/mod.rs +++ b/src/tool/mod.rs @@ -8,6 +8,7 @@ pub use icon_tool::IconTool; pub use room_tool::RoomTool; pub use wall_tool::WallTool; +use crate::button::Button; use crate::map_data::MapData; use crate::transform::Transform; use raylib::core::drawing::RaylibDrawHandle; @@ -28,4 +29,6 @@ pub trait Tool { fn active_update(&mut self, map: &mut MapData, rl: &RaylibHandle, transform: &Transform); fn draw(&self, _map: &MapData, _rld: &mut RaylibDrawHandle, _transform: &Transform) {} + + fn activation_key(&self) -> Button; } diff --git a/src/tool/room_tool.rs b/src/tool/room_tool.rs index 09126c9..1bfb225 100644 --- a/src/tool/room_tool.rs +++ b/src/tool/room_tool.rs @@ -1,4 +1,6 @@ use super::Tool; +use crate::button::Button; +use crate::config::{RoomToolKeybindings, ToolKeybindings}; use crate::grid::snap_to_grid; use crate::map_data::MapData; use crate::math::{Rect, Vec2}; @@ -8,6 +10,7 @@ use raylib::ffi::{Color, MouseButton}; use raylib::RaylibHandle; pub struct RoomTool { + keybindings: RoomToolKeybindings, /// The rectangle that is currently being drawn by the user. Once it is finished, it will be /// pushed into the room_rects. unfinished_rect: Option<(Vec2<f32>, Vec2<f32>)>, @@ -15,8 +18,9 @@ pub struct RoomTool { impl RoomTool { /// Create a new room tool where no rooms have been drawn yet. - pub fn new() -> Self { + pub fn new(keybindings: RoomToolKeybindings) -> Self { Self { + keybindings, unfinished_rect: None, } } @@ -31,18 +35,15 @@ impl Tool for RoomTool { } // Start or finish drawing the currently unfinished rectangle - if rl.is_mouse_button_pressed(MouseButton::MOUSE_LEFT_BUTTON) { - if let Some((pos1, pos2)) = self.unfinished_rect { - map_data.rooms_mut().push(Rect::bounding_rect(pos1, pos2)); - self.unfinished_rect = None; - } else { - let snapped_mouse_pos = snap_to_grid(mouse_pos_m, 0.5); - self.unfinished_rect = Some((snapped_mouse_pos, snapped_mouse_pos)) - } + if self.keybindings.finish_draw.is_pressed(rl) && self.unfinished_rect.is_some() { + let (pos1, pos2) = self.unfinished_rect.take().unwrap(); + map_data.rooms_mut().push(Rect::bounding_rect(pos1, pos2)); + } else if self.keybindings.start_draw.is_pressed(rl) { + let snapped_mouse_pos = snap_to_grid(mouse_pos_m, 0.5); + self.unfinished_rect = Some((snapped_mouse_pos, snapped_mouse_pos)) } - // Abort drawing the room (if any) in case the right mouse button was pressed. - if rl.is_mouse_button_pressed(MouseButton::MOUSE_RIGHT_BUTTON) { + if self.keybindings.abort_draw.is_pressed(rl) { self.unfinished_rect = None; } } @@ -74,4 +75,8 @@ impl Tool for RoomTool { ); } } + + fn activation_key(&self) -> Button { + self.keybindings.activation_key() + } } diff --git a/src/tool/wall_tool.rs b/src/tool/wall_tool.rs index 5eda8e0..31a3770 100644 --- a/src/tool/wall_tool.rs +++ b/src/tool/wall_tool.rs @@ -1,4 +1,6 @@ use super::Tool; +use crate::button::Button; +use crate::config::{ToolKeybindings, WallToolKeybindings}; use crate::grid::snap_to_grid; use crate::map_data::MapData; use crate::math::Vec2; @@ -8,12 +10,14 @@ use raylib::ffi::{Color, MouseButton, Vector2}; use raylib::RaylibHandle; pub struct WallTool { + keybindings: WallToolKeybindings, unfinished_wall: Option<(Vec2<f32>, Vec2<f32>)>, } impl WallTool { - pub fn new() -> Self { + pub fn new(keybindings: WallToolKeybindings) -> Self { Self { + keybindings, unfinished_wall: None, } } @@ -26,17 +30,16 @@ impl Tool for WallTool { *pos2 = snap_to_grid(mouse_pos_m, 0.5); } - if rl.is_mouse_button_pressed(MouseButton::MOUSE_LEFT_BUTTON) { - if let Some((pos1, pos2)) = self.unfinished_wall { - map_data.walls_mut().push((pos1, pos2)); - self.unfinished_wall = Some((pos2, pos2)); - } else { - let snapped_mouse_pos = snap_to_grid(mouse_pos_m, 0.5); - self.unfinished_wall = Some((snapped_mouse_pos, snapped_mouse_pos)) - } + if self.keybindings.finish_segment.is_pressed(rl) && self.unfinished_wall.is_some() { + let (pos1, pos2) = self.unfinished_wall.unwrap(); + map_data.walls_mut().push((pos1, pos2)); + self.unfinished_wall = Some((pos2, pos2)); + } else if self.keybindings.start_wall.is_pressed(rl) { + let snapped_mouse_pos = snap_to_grid(mouse_pos_m, 0.5); + self.unfinished_wall = Some((snapped_mouse_pos, snapped_mouse_pos)) } - if rl.is_mouse_button_pressed(MouseButton::MOUSE_RIGHT_BUTTON) { + if self.keybindings.abort_segment.is_pressed(rl) { self.unfinished_wall = None; } } @@ -74,4 +77,8 @@ impl Tool for WallTool { ); } } + + fn activation_key(&self) -> Button { + self.keybindings.activation_key() + } } |
