veloren_voxygen/settings/
control.rs

1use 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// ControlSetting-like struct used by Serde, to handle not serializing/building
11// post-deserializing the inverse_keybindings hashmap
12#[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        // Do a delta between default() ControlSettings and the argument, and let
22        // keybindings be only the custom keybindings chosen by the user.
23        for (k, v) in control_settings.keybindings {
24            if ControlSettings::default_binding(k) != v {
25                // Keybinding chosen by the user
26                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/// `ControlSettings` contains keybindings.
40#[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>>, // used in event loop
45}
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/// Since Macbook trackpads lack middle click, on OS X we default to Shift
62/// instead. It is an imperfect heuristic, but hopefully it will be a slightly
63/// better default, and the two places we default to middle click currently
64/// (roll and wall jump) are both situations where you cannot glide (the other
65/// default mapping for Shift).
66#[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        // For the KeyMouse->GameInput hashmap, we first need to remove the GameInput
101        // from the old binding
102        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        // then we add the GameInput to the proper key
109        self.inverse_keybindings
110            .entry(key_mouse.clone())
111            .or_default()
112            .insert(game_input);
113        // For the GameInput->KeyMouse hashmap, just overwrite the value
114        self.keybindings.insert(game_input, Some(key_mouse));
115    }
116
117    /// Return true if this key is used for multiple GameInputs that aren't
118    /// expected to be safe to have bound to the same key at the same time
119    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        // If a new GameInput is added, be sure to update GameInput::iterator() too!
136        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        // Sets the initial keybindings for those GameInputs.
230        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}