diff --git a/CHANGELOG.md b/CHANGELOG.md index 1177557408..5ec9bb5b61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Worldgen wildlife density modifier in features.ron - Rivers now make ambient sounds (again) - Added a setting to see own speech bubbles +- Added an option to allow players to remove keybindings ### Changed diff --git a/assets/voxygen/i18n/en/main.ron b/assets/voxygen/i18n/en/main.ron index 46e73a289b..b610b8e7e5 100644 --- a/assets/voxygen/i18n/en/main.ron +++ b/assets/voxygen/i18n/en/main.ron @@ -10,6 +10,7 @@ "main.connecting": "Connecting", "main.creating_world": "Creating world", "main.tip": "Tip:", + "main.unbound_key_tip": "unbound", // Welcome notice that appears the first time Veloren is started "main.notice": r#"Welcome to the alpha version of Veloren! diff --git a/voxygen/src/hud/settings_window/controls.rs b/voxygen/src/hud/settings_window/controls.rs index 005ba006b6..e92e54a262 100644 --- a/voxygen/src/hud/settings_window/controls.rs +++ b/voxygen/src/hud/settings_window/controls.rs @@ -22,6 +22,7 @@ widget_ids! { window_r, window_scrollbar, reset_controls_button, + keybinding_mode_button, controls_alignment_rectangle, controls_texts[], controls_buttons[], @@ -178,7 +179,11 @@ impl<'a> Widget for Controls<'a> { .set(button_id, ui) .was_clicked() { - events.push(ChangeBinding(game_input)); + if self.global_state.window.keybinding_mode { + events.push(ChangeBinding(game_input)); + } else { + events.push(RemoveBinding(game_input)); + } } // Set the previous id to the current one for the next cycle previous_element_id = Some(text_id); @@ -204,6 +209,27 @@ impl<'a> Widget for Controls<'a> { previous_element_id = Some(state.ids.reset_controls_button) } + let toggle_widget = Button::new() + .label(if self.global_state.window.keybinding_mode { + "remap" + } else { + "clear" + }) + .label_color(TEXT_COLOR) + .label_font_id(self.fonts.cyri.conrod_id) + .label_font_size(self.fonts.cyri.scale(15)) + .w(100.0) + .rgba(0.0, 0.0, 0.0, 0.0) + .border_rgba(0.0, 0.0, 0.0, 255.0) + .label_y(Relative::Scalar(1.0)); + if toggle_widget + .top_right_with_margins_on(state.ids.window, 10.0, 15.0) + .set(state.ids.keybinding_mode_button, ui) + .was_clicked() + { + events.push(ToggleKeybindingMode); + } + // Add an empty text widget to simulate some bottom margin, because conrod sucks if let Some(prev_id) = previous_element_id { Rectangle::fill_with([1.0, 1.0], color::TRANSPARENT) diff --git a/voxygen/src/menu/main/ui/connecting.rs b/voxygen/src/menu/main/ui/connecting.rs index 2b679cfdb0..e99d64b973 100644 --- a/voxygen/src/menu/main/ui/connecting.rs +++ b/voxygen/src/menu/main/ui/connecting.rs @@ -114,9 +114,15 @@ impl Screen { if let Ok(game_input) = GameInput::from_str(&tip[start + 1..end]) { new_tip.push_str(&tip[last_index..start]); new_tip.push_str( - controls.keybindings[&game_input] - .display_string(key_layout) - .as_str(), + match controls.keybindings.get(&game_input) { + Some(Some(key_mouse)) => { + key_mouse.display_string(key_layout) + }, + Some(None) => i18n.get("main.unbound_key_tip").to_string(), + None => ControlSettings::default_binding(game_input) + .display_string(key_layout), + } + .as_str(), ); last_index = end + 1; } diff --git a/voxygen/src/session/settings_change.rs b/voxygen/src/session/settings_change.rs index cbe0814e6a..2f0dd5b2b2 100644 --- a/voxygen/src/session/settings_change.rs +++ b/voxygen/src/session/settings_change.rs @@ -39,6 +39,8 @@ pub enum Chat { #[derive(Clone)] pub enum Control { ChangeBinding(GameInput), + RemoveBinding(GameInput), + ToggleKeybindingMode, ResetKeyBindings, } #[derive(Clone)] @@ -249,6 +251,12 @@ impl SettingsChange { Control::ChangeBinding(game_input) => { global_state.window.set_keybinding_mode(game_input); }, + Control::RemoveBinding(game_input) => { + settings.controls.remove_binding(game_input); + }, + Control::ToggleKeybindingMode => { + global_state.window.toggle_keybinding_mode(); + }, Control::ResetKeyBindings => { settings.controls = ControlSettings::default(); }, diff --git a/voxygen/src/settings/control.rs b/voxygen/src/settings/control.rs index faa53ba8f1..d9a4a62392 100644 --- a/voxygen/src/settings/control.rs +++ b/voxygen/src/settings/control.rs @@ -8,16 +8,16 @@ use winit::event::{MouseButton, VirtualKeyCode}; // post-deserializing the inverse_keybindings hashmap #[derive(Serialize, Deserialize)] struct ControlSettingsSerde { - keybindings: HashMap, + keybindings: HashMap>, } impl From for ControlSettingsSerde { fn from(control_settings: ControlSettings) -> Self { - let mut user_bindings: HashMap = HashMap::new(); + let mut user_bindings: HashMap> = HashMap::new(); // Do a delta between default() ControlSettings and the argument, and let // keybindings be only the custom keybindings chosen by the user. for (k, v) in control_settings.keybindings { - if ControlSettings::default_binding(k) != v { + if Some(ControlSettings::default_binding(k)) != v { // Keybinding chosen by the user user_bindings.insert(k, v); } @@ -32,7 +32,7 @@ impl From for ControlSettingsSerde { #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(from = "ControlSettingsSerde", into = "ControlSettingsSerde")] pub struct ControlSettings { - pub keybindings: HashMap, + pub keybindings: HashMap>, pub inverse_keybindings: HashMap>, // used in event loop } @@ -40,8 +40,11 @@ impl From for ControlSettings { fn from(control_serde: ControlSettingsSerde) -> Self { let user_keybindings = control_serde.keybindings; let mut control_settings = ControlSettings::default(); - for (k, v) in user_keybindings { - control_settings.modify_binding(k, v); + for (k, maybe_v) in user_keybindings { + match maybe_v { + Some(v) => control_settings.modify_binding(k, v), + None => control_settings.remove_binding(k), + } } control_settings } @@ -58,8 +61,19 @@ const MIDDLE_CLICK_KEY: KeyMouse = KeyMouse::Key(VirtualKeyCode::Grave); const MIDDLE_CLICK_KEY: KeyMouse = KeyMouse::Mouse(MouseButton::Middle); impl ControlSettings { + pub fn remove_binding(&mut self, game_input: GameInput) { + if let Some(inverse) = self + .keybindings + .insert(game_input, None) + .flatten() + .and_then(|key_mouse| self.inverse_keybindings.get_mut(&key_mouse)) + { + inverse.remove(&game_input); + } + } + pub fn get_binding(&self, game_input: GameInput) -> Option { - self.keybindings.get(&game_input).copied() + self.keybindings.get(&game_input).copied().flatten() } pub fn get_associated_game_inputs(&self, key_mouse: &KeyMouse) -> Option<&HashSet> { @@ -67,7 +81,7 @@ impl ControlSettings { } pub fn insert_binding(&mut self, game_input: GameInput, key_mouse: KeyMouse) { - self.keybindings.insert(game_input, key_mouse); + self.keybindings.insert(game_input, Some(key_mouse)); self.inverse_keybindings .entry(key_mouse) .or_default() @@ -89,7 +103,7 @@ impl ControlSettings { .or_default() .insert(game_input); // For the GameInput->KeyMouse hashmap, just overwrite the value - self.keybindings.insert(game_input, key_mouse); + self.keybindings.insert(game_input, Some(key_mouse)); } /// Return true if this key is used for multiple GameInputs that aren't diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index 9ff56f6f72..661edc1a7e 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -385,6 +385,8 @@ pub struct Window { needs_refresh_resize: bool, keypress_map: HashMap, pub remapping_keybindings: Option, + //true for remapping keybinds, false for clearing keybinds + pub keybinding_mode: bool, events: Vec, pub focused: bool, gilrs: Option, @@ -486,6 +488,7 @@ impl Window { needs_refresh_resize: false, keypress_map, remapping_keybindings: None, + keybinding_mode: true, events: Vec::new(), focused: true, gilrs, @@ -1329,6 +1332,8 @@ impl Window { self.remapping_keybindings = Some(game_input); } + pub fn toggle_keybinding_mode(&mut self) { self.keybinding_mode = !self.keybinding_mode; } + pub fn window(&self) -> &winit::window::Window { &self.window } pub fn modifiers(&self) -> winit::event::ModifiersState { self.modifiers }