support removing key bindings

This commit is contained in:
unvariant 2022-01-07 20:30:30 +00:00 committed by Imbris
parent e12390b08b
commit 71d2be5f76
7 changed files with 74 additions and 13 deletions

View File

@ -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 - Worldgen wildlife density modifier in features.ron
- Rivers now make ambient sounds (again) - Rivers now make ambient sounds (again)
- Added a setting to see own speech bubbles - Added a setting to see own speech bubbles
- Added an option to allow players to remove keybindings
### Changed ### Changed

View File

@ -10,6 +10,7 @@
"main.connecting": "Connecting", "main.connecting": "Connecting",
"main.creating_world": "Creating world", "main.creating_world": "Creating world",
"main.tip": "Tip:", "main.tip": "Tip:",
"main.unbound_key_tip": "unbound",
// Welcome notice that appears the first time Veloren is started // Welcome notice that appears the first time Veloren is started
"main.notice": r#"Welcome to the alpha version of Veloren! "main.notice": r#"Welcome to the alpha version of Veloren!

View File

@ -22,6 +22,7 @@ widget_ids! {
window_r, window_r,
window_scrollbar, window_scrollbar,
reset_controls_button, reset_controls_button,
keybinding_mode_button,
controls_alignment_rectangle, controls_alignment_rectangle,
controls_texts[], controls_texts[],
controls_buttons[], controls_buttons[],
@ -178,7 +179,11 @@ impl<'a> Widget for Controls<'a> {
.set(button_id, ui) .set(button_id, ui)
.was_clicked() .was_clicked()
{ {
if self.global_state.window.keybinding_mode {
events.push(ChangeBinding(game_input)); events.push(ChangeBinding(game_input));
} else {
events.push(RemoveBinding(game_input));
}
} }
// Set the previous id to the current one for the next cycle // Set the previous id to the current one for the next cycle
previous_element_id = Some(text_id); 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) 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 // Add an empty text widget to simulate some bottom margin, because conrod sucks
if let Some(prev_id) = previous_element_id { if let Some(prev_id) = previous_element_id {
Rectangle::fill_with([1.0, 1.0], color::TRANSPARENT) Rectangle::fill_with([1.0, 1.0], color::TRANSPARENT)

View File

@ -114,8 +114,14 @@ impl Screen {
if let Ok(game_input) = GameInput::from_str(&tip[start + 1..end]) { if let Ok(game_input) = GameInput::from_str(&tip[start + 1..end]) {
new_tip.push_str(&tip[last_index..start]); new_tip.push_str(&tip[last_index..start]);
new_tip.push_str( new_tip.push_str(
controls.keybindings[&game_input] match controls.keybindings.get(&game_input) {
.display_string(key_layout) 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(), .as_str(),
); );
last_index = end + 1; last_index = end + 1;

View File

@ -39,6 +39,8 @@ pub enum Chat {
#[derive(Clone)] #[derive(Clone)]
pub enum Control { pub enum Control {
ChangeBinding(GameInput), ChangeBinding(GameInput),
RemoveBinding(GameInput),
ToggleKeybindingMode,
ResetKeyBindings, ResetKeyBindings,
} }
#[derive(Clone)] #[derive(Clone)]
@ -249,6 +251,12 @@ impl SettingsChange {
Control::ChangeBinding(game_input) => { Control::ChangeBinding(game_input) => {
global_state.window.set_keybinding_mode(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 => { Control::ResetKeyBindings => {
settings.controls = ControlSettings::default(); settings.controls = ControlSettings::default();
}, },

View File

@ -8,16 +8,16 @@ use winit::event::{MouseButton, VirtualKeyCode};
// post-deserializing the inverse_keybindings hashmap // post-deserializing the inverse_keybindings hashmap
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
struct ControlSettingsSerde { struct ControlSettingsSerde {
keybindings: HashMap<GameInput, KeyMouse>, keybindings: HashMap<GameInput, Option<KeyMouse>>,
} }
impl From<ControlSettings> for ControlSettingsSerde { impl From<ControlSettings> for ControlSettingsSerde {
fn from(control_settings: ControlSettings) -> Self { fn from(control_settings: ControlSettings) -> Self {
let mut user_bindings: HashMap<GameInput, KeyMouse> = HashMap::new(); let mut user_bindings: HashMap<GameInput, Option<KeyMouse>> = HashMap::new();
// Do a delta between default() ControlSettings and the argument, and let // Do a delta between default() ControlSettings and the argument, and let
// keybindings be only the custom keybindings chosen by the user. // keybindings be only the custom keybindings chosen by the user.
for (k, v) in control_settings.keybindings { 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 // Keybinding chosen by the user
user_bindings.insert(k, v); user_bindings.insert(k, v);
} }
@ -32,7 +32,7 @@ impl From<ControlSettings> for ControlSettingsSerde {
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(from = "ControlSettingsSerde", into = "ControlSettingsSerde")] #[serde(from = "ControlSettingsSerde", into = "ControlSettingsSerde")]
pub struct ControlSettings { pub struct ControlSettings {
pub keybindings: HashMap<GameInput, KeyMouse>, pub keybindings: HashMap<GameInput, Option<KeyMouse>>,
pub inverse_keybindings: HashMap<KeyMouse, HashSet<GameInput>>, // used in event loop pub inverse_keybindings: HashMap<KeyMouse, HashSet<GameInput>>, // used in event loop
} }
@ -40,8 +40,11 @@ impl From<ControlSettingsSerde> for ControlSettings {
fn from(control_serde: ControlSettingsSerde) -> Self { fn from(control_serde: ControlSettingsSerde) -> Self {
let user_keybindings = control_serde.keybindings; let user_keybindings = control_serde.keybindings;
let mut control_settings = ControlSettings::default(); let mut control_settings = ControlSettings::default();
for (k, v) in user_keybindings { for (k, maybe_v) in user_keybindings {
control_settings.modify_binding(k, v); match maybe_v {
Some(v) => control_settings.modify_binding(k, v),
None => control_settings.remove_binding(k),
}
} }
control_settings control_settings
} }
@ -58,8 +61,19 @@ const MIDDLE_CLICK_KEY: KeyMouse = KeyMouse::Key(VirtualKeyCode::Grave);
const MIDDLE_CLICK_KEY: KeyMouse = KeyMouse::Mouse(MouseButton::Middle); const MIDDLE_CLICK_KEY: KeyMouse = KeyMouse::Mouse(MouseButton::Middle);
impl ControlSettings { 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<KeyMouse> { pub fn get_binding(&self, game_input: GameInput) -> Option<KeyMouse> {
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<GameInput>> { pub fn get_associated_game_inputs(&self, key_mouse: &KeyMouse) -> Option<&HashSet<GameInput>> {
@ -67,7 +81,7 @@ impl ControlSettings {
} }
pub fn insert_binding(&mut self, game_input: GameInput, key_mouse: KeyMouse) { 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 self.inverse_keybindings
.entry(key_mouse) .entry(key_mouse)
.or_default() .or_default()
@ -89,7 +103,7 @@ impl ControlSettings {
.or_default() .or_default()
.insert(game_input); .insert(game_input);
// For the GameInput->KeyMouse hashmap, just overwrite the value // 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 /// Return true if this key is used for multiple GameInputs that aren't

View File

@ -385,6 +385,8 @@ pub struct Window {
needs_refresh_resize: bool, needs_refresh_resize: bool,
keypress_map: HashMap<GameInput, winit::event::ElementState>, keypress_map: HashMap<GameInput, winit::event::ElementState>,
pub remapping_keybindings: Option<GameInput>, pub remapping_keybindings: Option<GameInput>,
//true for remapping keybinds, false for clearing keybinds
pub keybinding_mode: bool,
events: Vec<Event>, events: Vec<Event>,
pub focused: bool, pub focused: bool,
gilrs: Option<Gilrs>, gilrs: Option<Gilrs>,
@ -486,6 +488,7 @@ impl Window {
needs_refresh_resize: false, needs_refresh_resize: false,
keypress_map, keypress_map,
remapping_keybindings: None, remapping_keybindings: None,
keybinding_mode: true,
events: Vec::new(), events: Vec::new(),
focused: true, focused: true,
gilrs, gilrs,
@ -1329,6 +1332,8 @@ impl Window {
self.remapping_keybindings = Some(game_input); 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 window(&self) -> &winit::window::Window { &self.window }
pub fn modifiers(&self) -> winit::event::ModifiersState { self.modifiers } pub fn modifiers(&self) -> winit::event::ModifiersState { self.modifiers }