veloren_voxygen/settings/
control.rs1use crate::{game_input::GameInput, window::KeyMouse};
2use hashbrown::{HashMap, HashSet};
3use serde::{Deserialize, Serialize};
4use strum::IntoEnumIterator;
5use winit::{
6 event::MouseButton,
7 keyboard::{Key, NamedKey},
8};
9
10#[derive(Serialize, Deserialize)]
13#[serde(default)]
14struct ControlSettingsSerde {
15 keybindings: HashMap<GameInput, Option<KeyMouse>>,
16}
17
18impl From<ControlSettings> for ControlSettingsSerde {
19 fn from(control_settings: ControlSettings) -> Self {
20 let mut user_bindings: HashMap<GameInput, Option<KeyMouse>> = HashMap::new();
21 for (k, v) in control_settings.keybindings {
24 if ControlSettings::default_binding(k) != v {
25 user_bindings.insert(k, v);
27 }
28 }
29 ControlSettingsSerde {
30 keybindings: user_bindings,
31 }
32 }
33}
34
35impl Default for ControlSettingsSerde {
36 fn default() -> Self { ControlSettings::default().into() }
37}
38
39#[derive(Clone, Debug, Serialize, Deserialize)]
41#[serde(from = "ControlSettingsSerde", into = "ControlSettingsSerde")]
42pub struct ControlSettings {
43 pub keybindings: HashMap<GameInput, Option<KeyMouse>>,
44 pub inverse_keybindings: HashMap<KeyMouse, HashSet<GameInput>>, }
46
47impl From<ControlSettingsSerde> for ControlSettings {
48 fn from(control_serde: ControlSettingsSerde) -> Self {
49 let user_keybindings = control_serde.keybindings;
50 let mut control_settings = ControlSettings::default();
51 for (k, maybe_v) in user_keybindings {
52 match maybe_v {
53 Some(v) => control_settings.modify_binding(k, v),
54 None => control_settings.remove_binding(k),
55 }
56 }
57 control_settings
58 }
59}
60
61#[cfg(target_os = "macos")]
67const MIDDLE_CLICK_KEY: KeyMouse = KeyMouse::Key(Key::Named(NamedKey::Shift));
68#[cfg(not(target_os = "macos"))]
69const MIDDLE_CLICK_KEY: KeyMouse = KeyMouse::Mouse(MouseButton::Middle);
70
71impl ControlSettings {
72 pub fn remove_binding(&mut self, game_input: GameInput) {
73 if let Some(inverse) = self
74 .keybindings
75 .insert(game_input, None)
76 .flatten()
77 .and_then(|key_mouse| self.inverse_keybindings.get_mut(&key_mouse))
78 {
79 inverse.remove(&game_input);
80 }
81 }
82
83 pub fn get_binding(&self, game_input: GameInput) -> Option<KeyMouse> {
84 self.keybindings.get(&game_input).cloned().flatten()
85 }
86
87 pub fn get_associated_game_inputs(&self, key_mouse: &KeyMouse) -> Option<&HashSet<GameInput>> {
88 self.inverse_keybindings.get(key_mouse)
89 }
90
91 pub fn insert_binding(&mut self, game_input: GameInput, key_mouse: KeyMouse) {
92 self.keybindings.insert(game_input, Some(key_mouse.clone()));
93 self.inverse_keybindings
94 .entry(key_mouse)
95 .or_default()
96 .insert(game_input);
97 }
98
99 pub fn modify_binding(&mut self, game_input: GameInput, key_mouse: KeyMouse) {
100 if let Some(old_binding) = self.get_binding(game_input) {
103 self.inverse_keybindings
104 .entry(old_binding)
105 .or_default()
106 .remove(&game_input);
107 }
108 self.inverse_keybindings
110 .entry(key_mouse.clone())
111 .or_default()
112 .insert(game_input);
113 self.keybindings.insert(game_input, Some(key_mouse));
115 }
116
117 pub fn has_conflicting_bindings(&self, key_mouse: KeyMouse) -> bool {
120 if let Some(game_inputs) = self.inverse_keybindings.get(&key_mouse) {
121 for a in game_inputs.iter() {
122 for b in game_inputs.iter() {
123 if !GameInput::can_share_bindings(*a, *b) {
124 return true;
125 }
126 }
127 }
128 }
129 false
130 }
131
132 pub fn default_binding(game_input: GameInput) -> Option<KeyMouse> {
133 let char = |s| Key::Character(winit::keyboard::SmolStr::new(s));
134
135 Some(KeyMouse::Key(match game_input {
137 GameInput::Primary => return Some(KeyMouse::Mouse(MouseButton::Left)),
138 GameInput::Secondary => return Some(KeyMouse::Mouse(MouseButton::Right)),
139 GameInput::Block => Key::Named(NamedKey::Alt),
140 GameInput::ToggleCursor => char(","),
141 GameInput::Escape => Key::Named(NamedKey::Escape),
142 GameInput::Chat => Key::Named(NamedKey::Enter),
143 GameInput::Command => char("/"),
144 GameInput::MoveForward => char("W"),
145 GameInput::MoveLeft => char("A"),
146 GameInput::MoveBack => char("S"),
147 GameInput::MoveRight => char("D"),
148 GameInput::Jump => Key::Named(NamedKey::Space),
149 GameInput::WallJump => Key::Named(NamedKey::Space),
150 GameInput::Sit => char("K"),
151 GameInput::Crawl => Key::Named(NamedKey::ArrowDown),
152 GameInput::Dance => char("J"),
153 GameInput::Greet => char("H"),
154 GameInput::Glide => Key::Named(NamedKey::Control),
155 GameInput::SwimUp => Key::Named(NamedKey::Space),
156 GameInput::SwimDown => Key::Named(NamedKey::Shift),
157 GameInput::Fly => char("H"),
158 GameInput::Sneak => Key::Named(NamedKey::Shift),
159 GameInput::CancelClimb => Key::Named(NamedKey::Shift),
160 GameInput::ToggleLantern => char("G"),
161 GameInput::Mount => char("F"),
162 GameInput::StayFollow => char("V"),
163 GameInput::Map => char("M"),
164 GameInput::Inventory => char("I"),
165 GameInput::Trade => char("T"),
166 GameInput::Social => char("O"),
167 GameInput::Crafting => char("C"),
168 GameInput::Diary => char("P"),
169 GameInput::Settings => Key::Named(NamedKey::F10),
170 GameInput::Controls => Key::Named(NamedKey::F1),
171 GameInput::ToggleInterface => Key::Named(NamedKey::F2),
172 GameInput::ToggleDebug => Key::Named(NamedKey::F3),
173 #[cfg(feature = "egui-ui")]
174 GameInput::ToggleEguiDebug => Key::Named(NamedKey::F7),
175 GameInput::ToggleChat => Key::Named(NamedKey::F5),
176 GameInput::Fullscreen => Key::Named(NamedKey::F11),
177 GameInput::Screenshot => Key::Named(NamedKey::F4),
178 GameInput::ToggleIngameUi => Key::Named(NamedKey::F6),
179 GameInput::Roll => return Some(MIDDLE_CLICK_KEY),
180 GameInput::GiveUp => Key::Named(NamedKey::Space),
181 GameInput::Respawn => Key::Named(NamedKey::Space),
182 GameInput::Interact => char("E"),
183 GameInput::ToggleWield => char("R"),
184 GameInput::FreeLook => char("L"),
185 GameInput::AutoWalk => char("."),
186 GameInput::ZoomIn => char(")"),
187 GameInput::ZoomOut => char("("),
188 GameInput::ZoomLock => return None,
189 GameInput::CameraClamp => char("'"),
190 GameInput::CycleCamera => char("0"),
191 GameInput::Slot1 => char("1"),
192 GameInput::Slot2 => char("2"),
193 GameInput::Slot3 => char("3"),
194 GameInput::Slot4 => char("4"),
195 GameInput::Slot5 => char("5"),
196 GameInput::Slot6 => char("6"),
197 GameInput::Slot7 => char("7"),
198 GameInput::Slot8 => char("8"),
199 GameInput::Slot9 => char("9"),
200 GameInput::Slot10 => char("Q"),
201 GameInput::NextSlot => return None,
202 GameInput::PreviousSlot => return None,
203 GameInput::CurrentSlot => return None,
204 GameInput::SwapLoadout => Key::Named(NamedKey::Tab),
205 GameInput::Select => char("X"),
206 GameInput::AcceptGroupInvite => char("Y"),
207 GameInput::DeclineGroupInvite => char("N"),
208 GameInput::MapZoomIn => char("+"),
209 GameInput::MapZoomOut => char("-"),
210 GameInput::MapSetMarker => return Some(KeyMouse::Mouse(MouseButton::Middle)),
211 GameInput::SpectateSpeedBoost => Key::Named(NamedKey::Control),
212 GameInput::SpectateViewpoint => return Some(KeyMouse::Mouse(MouseButton::Middle)),
213 GameInput::MuteMaster => Key::Named(NamedKey::AudioVolumeMute),
214 GameInput::MuteInactiveMaster => return None,
215 GameInput::MuteMusic => Key::Named(NamedKey::F8),
216 GameInput::MuteSfx => return None,
217 GameInput::MuteAmbience => return None,
218 GameInput::ToggleWalk => char("B"),
219 }))
220 }
221}
222
223impl Default for ControlSettings {
224 fn default() -> Self {
225 let mut new_settings = Self {
226 keybindings: HashMap::new(),
227 inverse_keybindings: HashMap::new(),
228 };
229 for game_input in GameInput::iter() {
231 match ControlSettings::default_binding(game_input) {
232 None => {},
233 Some(default) => new_settings.insert_binding(game_input, default),
234 };
235 }
236 new_settings
237 }
238}