This repository was archived by the owner on Jun 25, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathKeyboardStringReader.cs
More file actions
265 lines (228 loc) · 10.7 KB
/
KeyboardStringReader.cs
File metadata and controls
265 lines (228 loc) · 10.7 KB
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/*
Copyright (C) 2011 by Hunter Haydel
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
namespace Wedge.XNA.Input
{
public class KeyboardStringReader
{
#region Key Maps
// Not super happy about having to do this, but it was either this or a giant switch statement and this kept
// the method cleaner.
private static Dictionary<Keys, Tuple<char, char>> alphabetKeyMap = new Dictionary<Keys, Tuple<char, char>>
{
{Keys.Q, new Tuple<char, char>('q', 'Q')},
{Keys.W, new Tuple<char, char>('w', 'W')},
{Keys.E, new Tuple<char, char>('e', 'E')},
{Keys.R, new Tuple<char, char>('r', 'R')},
{Keys.T, new Tuple<char, char>('t', 'T')},
{Keys.Y, new Tuple<char, char>('y', 'Y')},
{Keys.U, new Tuple<char, char>('u', 'U')},
{Keys.I, new Tuple<char, char>('i', 'I')},
{Keys.O, new Tuple<char, char>('o', 'O')},
{Keys.P, new Tuple<char, char>('p', 'P')},
{Keys.A, new Tuple<char, char>('a', 'A')},
{Keys.S, new Tuple<char, char>('s', 'S')},
{Keys.D, new Tuple<char, char>('d', 'D')},
{Keys.F, new Tuple<char, char>('f', 'F')},
{Keys.G, new Tuple<char, char>('g', 'G')},
{Keys.H, new Tuple<char, char>('h', 'H')},
{Keys.J, new Tuple<char, char>('j', 'J')},
{Keys.K, new Tuple<char, char>('k', 'K')},
{Keys.L, new Tuple<char, char>('l', 'L')},
{Keys.Z, new Tuple<char, char>('z', 'Z')},
{Keys.X, new Tuple<char, char>('x', 'X')},
{Keys.C, new Tuple<char, char>('c', 'C')},
{Keys.V, new Tuple<char, char>('v', 'V')},
{Keys.B, new Tuple<char, char>('b', 'B')},
{Keys.N, new Tuple<char, char>('n', 'N')},
{Keys.M, new Tuple<char, char>('m', 'M')}
};
private static Dictionary<Keys, Tuple<char, char>> symbolNumberKeyMap = new Dictionary<Keys, Tuple<char, char>>
{
{Keys.OemTilde, new Tuple<char, char>('`', '~')},
{Keys.D1, new Tuple<char, char>('1', '!')},
{Keys.D2, new Tuple<char, char>('2', '@')},
{Keys.D3, new Tuple<char, char>('3', '#')},
{Keys.D4, new Tuple<char, char>('4', '$')},
{Keys.D5, new Tuple<char, char>('5', '%')},
{Keys.D6, new Tuple<char, char>('6', '^')},
{Keys.D7, new Tuple<char, char>('7', '&')},
{Keys.D8, new Tuple<char, char>('8', '*')},
{Keys.D9, new Tuple<char, char>('9', '(')},
{Keys.D0, new Tuple<char, char>('0', ')')},
{Keys.OemMinus, new Tuple<char, char>('-', '_')},
{Keys.OemPlus, new Tuple<char, char>('+', '=')},
{Keys.OemOpenBrackets, new Tuple<char, char>('[', '{')},
{Keys.OemCloseBrackets, new Tuple<char, char>(']', '}')},
{Keys.OemPipe, new Tuple<char, char>('\\', '|')},
{Keys.OemSemicolon, new Tuple<char, char>(';', ':')},
{Keys.OemQuotes, new Tuple<char, char>('\'', '"')},
{Keys.OemComma, new Tuple<char, char>(',', '<')},
{Keys.OemPeriod, new Tuple<char, char>('.', '>')},
{Keys.OemQuestion, new Tuple<char, char>('/', '?')},
{Keys.Space, new Tuple<char, char>(' ', ' ')}
};
private static Dictionary<Keys, char> numPadKeyMap = new Dictionary<Keys, char>
{
{Keys.NumPad0, '0'},
{Keys.NumPad1, '1'},
{Keys.NumPad2, '2'},
{Keys.NumPad3, '3'},
{Keys.NumPad4, '4'},
{Keys.NumPad5, '5'},
{Keys.NumPad6, '6'},
{Keys.NumPad7, '7'},
{Keys.NumPad8, '8'},
{Keys.NumPad9, '9'},
{Keys.Decimal, '.'}
};
private static Dictionary<Keys, char> numPadMathKeyMap = new Dictionary<Keys, char>
{
{Keys.Divide, '/'},
{Keys.Multiply, '*'},
{Keys.Subtract, '-'},
{Keys.Add, '+'}
};
#endregion
#region Private Members
// Dictionary to keep track of when each pressed key should repeat.
private Dictionary<Keys, double> pressedKeys;
#endregion
#region Properties
/// <summary>
/// The amount of time in milliseconds a key needs to be held down to repeat for
/// the first time.
/// </summary>
public int FirstRepeatKeyInterval
{
get;
set;
}
/// <summary>
/// The amount of time in milliseconds a key needs to be held down to repeat for
/// the second time and beyond.
/// </summary>
public int RepeatKeyInterval
{
get;
set;
}
#endregion
#region Constructor
/// <summary>
/// Initializes a new instance of the KeyboardStringBuilder class.
/// </summary>
public KeyboardStringReader()
{
this.FirstRepeatKeyInterval = 700;
this.RepeatKeyInterval = 100;
this.pressedKeys = new Dictionary<Keys, double>();
}
#endregion
#region Public Methods
/// <summary>
/// Process the current keyboard state and add or remove characters from the given StringBuilder.
/// </summary>
/// <param name="keyboard">The keyboard state input is being read from.</param>
/// <param name="time">Current GameTime</param>
/// <param name="text">The StringBuilder to be modified based on keyboard state.</param>
public void Process(KeyboardState keyboard, GameTime time, StringBuilder text)
{
Keys[] keys = keyboard.GetPressedKeys();
// check and see if shift is down, caps lock is on, and/or num lock is on
bool shift = (keyboard.IsKeyDown(Keys.LeftShift) || keyboard.IsKeyDown(Keys.RightShift));
bool capsLock = System.Windows.Forms.Control.IsKeyLocked(System.Windows.Forms.Keys.CapsLock);
bool numLock = System.Windows.Forms.Control.IsKeyLocked(System.Windows.Forms.Keys.NumLock);
// remove any keys that aren't down anymore from pressed keys
foreach (Keys key in this.pressedKeys.Keys.Except(keys).ToArray())
{
this.pressedKeys.Remove(key);
}
foreach (Keys key in keys)
{
// if the key wasn't pressed the last go round process it and set the repeat time to the
// current time + the first repeat interval. Otherwise process it and set the repeat time
// to the current time + the repeat key interval.
if (!this.pressedKeys.Keys.Any(k => k == key))
{
ProcessKey(key, shift, capsLock, numLock, text);
this.pressedKeys[key] = time.TotalGameTime.TotalMilliseconds + this.FirstRepeatKeyInterval;
}
else if (time.TotalGameTime.TotalMilliseconds > this.pressedKeys[key])
{
ProcessKey(key, shift, capsLock, numLock, text);
this.pressedKeys[key] = time.TotalGameTime.TotalMilliseconds + this.RepeatKeyInterval;
}
}
}
#endregion
#region Private Methods
/// <summary>
/// Modifies the StringBuilder based on the pressed key.
/// </summary>
/// <param name="key">The key being pressed.</param>
/// <param name="shift">Is the shift key down or capslock on?</param>
/// <param name="capsLock">Is caps lock on?</param>
/// <param name="numLock">Is num lock on?</param>
/// <param name="text">The StringBuilder to be modified.</param>
private void ProcessKey(Keys key, bool shift, bool capsLock, bool numLock, StringBuilder text)
{
if (key == Keys.Back && text.Length > 0) text = text.Remove(text.Length - 1, 1);
char? newChar = GetCharacter(key, shift, capsLock, numLock);
if (newChar.HasValue) text.Append(newChar.Value);
}
/// <summary>
/// Gets the character associated with the given key/shift pair.
/// </summary>
/// <param name="key">The key being pressed.</param>
/// <param name="shift">Is the shift key down?</param>
/// <param name="capsLock">Is caps lock on?</param>
/// <param name="numLock">Is num lock on?</param>
/// <returns>The character the key/shift pair maps to.</returns>
private char? GetCharacter(Keys key, bool shift, bool capsLock, bool numLock)
{
char? newChar = new char?();
if (alphabetKeyMap.Keys.Contains(key))
{
Tuple<char, char> characterMap = alphabetKeyMap[key];
newChar = (shift ^ capsLock) ? characterMap.Item2 : characterMap.Item1;
}
else if (symbolNumberKeyMap.Keys.Contains(key))
{
Tuple<char, char> characterMap = symbolNumberKeyMap[key];
newChar = (shift) ? characterMap.Item2 : characterMap.Item1;
}
else if (numPadKeyMap.ContainsKey(key))
{
if (numLock) newChar = numPadKeyMap[key];
}
else if (numPadMathKeyMap.ContainsKey(key))
{
newChar = numPadMathKeyMap[key];
}
return newChar;
}
#endregion
}
}