aboutsummaryrefslogtreecommitdiff
path: root/src/config.rs
blob: 6d0680c5bc5f996e19eef233887e6328ae455c5c (plain) (blame)
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
//! Home of the user configuratable content of graf karto, like keybindings and (TODO) colours etc.

use crate::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(),
            },
        }
    }
}