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
use std::collections::HashMap;

use dces::prelude::Entity;

use crate::{shell::Key, theming::Theme};

#[derive(Default, Clone, Debug, PartialEq)]
/// The `Global` struct is used to define global `properties` that could be access application width.
pub struct Global {
    /// Contains the current focused widget.
    pub focused_widget: Option<Entity>,

    /// Used to reference widgets by its css id.
    pub id_map: HashMap<String, Entity>,

    /// Stores the state of the keyboard
    pub keyboard_state: KeyboardState,

    /// The current window theme
    pub theme: Theme,
}

/// Contains the state information for the keyboard.
///
/// This currently tracks which keys are currently pressed.
///
/// The key state is stored in a lazy-loaded HashMap.
///
/// There are several convenience methods to check common modifiers (ctrl, shift, alt, etc).
/// This is useful if you don't care which shift key is down.
#[derive(Default, Clone, Debug, PartialEq)]
pub struct KeyboardState {
    key_list: HashMap<Key, bool>,
}

impl KeyboardState {
    /// Sets whether or not the given key is currently pressed
    pub fn set_key_state(&mut self, key: Key, pressed: bool) {
        self.key_list.insert(key, pressed);
    }
    /// Returns whether or not the requested key is pressed
    pub fn is_key_down(&self, key: Key) -> bool {
        match self.key_list.get(&key) {
            // If we have the key on this list, return its state
            Some(item) => *item,
            // Otherwise, it hasn't been set as down
            None => false,
        }
    }
    /// Returns whether or not any shift key is down.
    pub fn is_shift_down(&self) -> bool {
        self.is_key_down(Key::ShiftL) || self.is_key_down(Key::ShiftR)
    }
    /// Returns whether or not any alt key is down.
    pub fn is_alt_down(&self) -> bool {
        self.is_key_down(Key::Alt)
    }

    /// Returns whether or not any control key is down.
    pub fn is_ctrl_down(&self) -> bool {
        self.is_key_down(Key::Control)
    }

    /// Returns whether or not any home key is down.
    pub fn is_home_down(&self) -> bool {
        self.is_key_down(Key::Home)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    /// A quick test to ensure that the items are properly set.
    fn basic_test() {
        let mut state = KeyboardState::default();
        // Do a quick check beforehand
        assert_eq!(state.is_key_down(Key::ShiftL), false);
        // Set the state down and immediately check
        state.set_key_state(Key::ShiftL, true);
        assert_eq!(state.is_key_down(Key::ShiftL), true);
        state.set_key_state(Key::ShiftL, false);
        assert_eq!(state.is_key_down(Key::ShiftL), false);
        // Set quit a few in a row
        state.set_key_state(Key::ShiftL, true);
        state.set_key_state(Key::ShiftR, true);
        state.set_key_state(Key::Space, true);
        state.set_key_state(Key::Control, true);
        state.set_key_state(Key::Alt, true);
        // Ensure each of these are still down
        assert_eq!(state.is_key_down(Key::ShiftL), true);
        assert_eq!(state.is_key_down(Key::ShiftR), true);
        assert_eq!(state.is_key_down(Key::Space), true);
        assert_eq!(state.is_key_down(Key::Control), true);
        assert_eq!(state.is_key_down(Key::Alt), true);
    }

    #[test]
    /// Test for the convenience methods
    fn test_convenience() {
        let mut state = KeyboardState::default();
        // Check to ensure they are all false
        assert_eq!(state.is_alt_down(), false);
        assert_eq!(state.is_ctrl_down(), false);
        assert_eq!(state.is_shift_down(), false);
        // Set ctrl and alt to true and check
        state.set_key_state(Key::Control, true);
        assert_eq!(state.is_ctrl_down(), true);

        state.set_key_state(Key::Alt, true);
        assert_eq!(state.is_alt_down(), true);

        assert_eq!(state.is_shift_down(), false);
        // Set shift (via L)
        state.set_key_state(Key::ShiftL, true);
        assert_eq!(state.is_shift_down(), true);
        // Set shift (via R and L both set)
        state.set_key_state(Key::ShiftR, true);
        assert_eq!(state.is_shift_down(), true);
        // Disable L Shift to ensure correct result with just R held
        state.set_key_state(Key::ShiftL, false);
        assert_eq!(state.is_shift_down(), true);
        // Disable both shift keys
        state.set_key_state(Key::ShiftR, false);
        assert_eq!(state.is_shift_down(), false);
        // Disable alt and ctrl and check again
        state.set_key_state(Key::Control, false);
        assert_eq!(state.is_ctrl_down(), false);

        state.set_key_state(Key::Alt, false);
        assert_eq!(state.is_alt_down(), false);
    }
}