aboutsummaryrefslogtreecommitdiff
path: root/src/client/config.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/config.rs')
-rw-r--r--src/client/config.rs166
1 files changed, 166 insertions, 0 deletions
diff --git a/src/client/config.rs b/src/client/config.rs
new file mode 100644
index 0000000..96ff3f5
--- /dev/null
+++ b/src/client/config.rs
@@ -0,0 +1,166 @@
+//! Home of the user configuratable content of graf karto, like keybindings and (TODO) colours etc.
+
+use crate::client::input::{Binding, Button, Input, MouseButton, Scancode};
+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;
+
+/// All configuration parameters the user can set are contained in this struct.
+#[allow(missing_docs)]
+#[derive(Deserialize, Serialize)]
+pub struct Config {
+ pub tool_activation_binds: ToolActivationBinds,
+ pub tool_general_binds: ToolGeneralBinds,
+ pub icon_binds: IconToolBinds,
+}
+
+#[allow(missing_docs)]
+#[derive(Deserialize, Serialize)]
+/// The keys used to activate the individual tools. These keystrokes will not be sent to the tools,
+/// but instead will be handled by the editor where the tools are registered.
+pub struct ToolActivationBinds {
+ pub deletion: Binding,
+ pub icon: Binding,
+ pub polygon_room: Binding,
+ pub rect_room: Binding,
+ pub selection: Binding,
+ pub wall: Binding,
+}
+
+#[derive(Deserialize, Serialize)]
+/// Keys that are useful to most tools. These are packaged so that not every tool has the same n keys
+/// and then some more.
+pub struct ToolGeneralBinds {
+ /// Keybinding to, where applicable, place a single node (usually a vertex) for the tool in
+ /// question.
+ pub place_single: Binding,
+ /// Finish up whatever one is doing with the current tool, without removing information.
+ pub finish: Binding,
+ /// Abort whatever one is doing with the current tool which means the last atomic action will not
+ /// be pushed into the map items.
+ pub abort: Binding,
+}
+
+#[derive(Clone, Serialize, Deserialize)]
+/// Key bindings that are individually interesting to the icon tool.
+pub struct IconToolBinds {
+ /// Key to change to the next icon of the icon list.
+ pub next: Binding,
+ /// Key to change to the previous icon of the icon list.
+ pub previous: Binding,
+ /// Rotate the working icon clockwise by a certain amount (currently 45 degrees)
+ pub rotate_clockwise: Binding,
+ /// Rotate the working icon counterclockwise by a certain amount (currently 45 degrees)
+ pub rotate_counterclockwise: Binding,
+}
+
+impl Config {
+ /// Try to parse a configuration from the file located at path.
+ ///
+ /// # Errors
+ /// If the file is not found or can not be read or parsed for a different reason, an IO-Error is
+ /// returned.
+ 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)),
+ }
+ }
+
+ /// Try to write the configuration to the file at path. If the file exists, it will be overwritten.
+ ///
+ /// # Errors
+ /// If the file can not be written, for example for lack of permissions, an IO-Error is returned.
+ 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())
+ }
+}
+
+/// Registers all bindings from the given configuration into the input handler. Should the
+/// configuration change at runtime, the global bindings of the input handler need to be cleared and
+/// this function must be called again.
+pub fn register_bindings(config: &Config, input: &mut Input) {
+ if !input.add_global(config.tool_activation_binds.deletion.clone()) {
+ warn!("Tried to add deletion binding twice.");
+ }
+ if !input.add_global(config.tool_activation_binds.icon.clone()) {
+ warn!("Tried to add icon binding twice.");
+ }
+ if !input.add_global(config.tool_activation_binds.polygon_room.clone()) {
+ warn!("Tried to add polygon room binding twice.");
+ }
+ if !input.add_global(config.tool_activation_binds.rect_room.clone()) {
+ warn!("Tried to add rect room binding twice.");
+ }
+ if !input.add_global(config.tool_activation_binds.selection.clone()) {
+ warn!("Tried to add selection binding twice.");
+ }
+ if !input.add_global(config.tool_activation_binds.wall.clone()) {
+ warn!("Tried to add wall binding twice.");
+ }
+ if !input.add_global(config.tool_general_binds.place_single.clone()) {
+ warn!("Tried to add place single binding twice.");
+ }
+ if !input.add_global(config.tool_general_binds.finish.clone()) {
+ warn!("Tried to add finish binding twice.");
+ }
+ if !input.add_global(config.tool_general_binds.abort.clone()) {
+ warn!("Tried to add abort binding twice.");
+ }
+ if !input.add_global(config.icon_binds.next.clone()) {
+ warn!("Tried to add next binding twice.");
+ }
+ if !input.add_global(config.icon_binds.previous.clone()) {
+ warn!("Tried to add previous binding twice.");
+ }
+ if !input.add_global(config.icon_binds.rotate_clockwise.clone()) {
+ warn!("Tried to add rotate clockwise binding twice.");
+ }
+ if !input.add_global(config.icon_binds.rotate_counterclockwise.clone()) {
+ warn!("Tried to add rotate counterclockwise binding twice.");
+ }
+}
+
+impl Default for Config {
+ fn default() -> Self {
+ Config {
+ tool_activation_binds: ToolActivationBinds {
+ deletion: Button::Text('d').into(),
+ icon: Button::Text('i').into(),
+ polygon_room: Button::Text('p').into(),
+ rect_room: Button::Text('r').into(),
+ selection: Button::Text('s').into(),
+ wall: Button::Text('w').into(),
+ },
+ tool_general_binds: ToolGeneralBinds {
+ place_single: Button::Mouse(MouseButton::Left).into(),
+ finish: Button::Scancode(Scancode::Enter).into(),
+ abort: Button::Mouse(MouseButton::Right).into(),
+ },
+ icon_binds: IconToolBinds {
+ next: Button::Text('j').into(),
+ previous: Button::Text('k').into(),
+ rotate_clockwise: Button::Text('+').into(),
+ rotate_counterclockwise: Button::Text('-').into(),
+ },
+ }
+ }
+}