![]() |
|
This thread is locked; no one can reply to it.
![]() ![]() |
1
2
|
Real-Time Text Input |
Arthur Kalliokoski
Second in Command
February 2005
![]() |
Thomas Fjellstrom said: You'd be surprised. Yep, right now I'd give They all watch too much MSNBC... they get ideas. |
Nazerith
Member #12,551
February 2011
|
[EDIT] Here is the first version of the class. I've not had a chance to test (or even compile it), so use at your own risk. I'm assuming you know how to set up an event queue and capture events. So assuming your variables are declared as follows: ALLEGRO_EVENT ev; ALLEGRO_EVENT_SOURCE* keyboard_source = al_get_keyboard_event_source(); TextField text; bool success; You should be able to capture and process keyboard actions with the following. It returns true if it succeeds, false if it fails (static field is full, can't delete due to cursor position, ect). It should properly handle SHIFT key, and returns null if you hold CTRL/ALT or press a command key (function keys, pause, break, prtsc, sysrq, esc, ect). That way you can handle those cases separately if you want. if(ev.source == keyboard_source) success = text.ProcessKey(ev); The following will return the current string in the input field. It returns a character array, which should be easy to render with al_draw_text or similar. str = text.CurrentString(); The class has lots of different helper functions. It support both dynamic and static string lengths. Obviously static is faster in some regards. Dynamic strings will grow automatically, but you can also use text.SetConserve(true) which will shrink the array if there is lots of dead space at the end (20+). By default the array doesn't shrink once its been expanded. So without further ado, the TextField class: 1class TextField
2{
3 char * tf_string; //current string
4 int tf_position; //cursor position
5 int tf_size; //size allocated for array
6 bool tf_isStatic; //defaults to false (dynamic allocation)
7 bool tf_conserveMemory; //if set dynamic arrays are shortened to conserve memory
8
9 bool AddCharacter(char c); //add character at current cursor position. (return true if success)
10 bool DeleteCharacter(bool reverse = false); //delete character at current position. (return true if success)
11
12public:
13 TextField(int maxLength=0); //initialize empty field
14 TextField(char * StartString, int StartPosition, int maxLength=0); //initialize field with starting string and cursor position
15
16 char * GetString(); //returns current string
17 int GetPosition(); //returns current cursor position
18 int GetSize(); //returns current size of array
19 bool IsConserve(); //returns if dynamic arrays are resized to conserve memory
20 bool IsStatic(); //returns if field has static length
21
22 bool SetPosition(int p); //set cursor position
23 void SetConserve(bool conserve); //set dynamic string memory conservation
24
25 bool ProcessKey(ALLEGRO_EVENT ev); //processes a key press (returns true if success, false if failure, null if ignored)
26};
27
28TextField::TextField(int maxLength=0)
29{
30 if(maxLength > 0) //if MaxLength was set greater than zero, array is Static length
31 {
32 tf_size = maxLength;
33 tf_isStatic = true;
34 }
35 else //set as dynamic with a starting 10 character storage
36 {
37 tf_size = 10;
38 tf_isStatic = false;
39 }
40 tf_string = new char[tf_size];
41 tf_position = 0;
42 tf_conserveMemory = false;
43}
44
45TextField::TextField(char * StartString, int StartPosition, int MaxLength=0)
46{
47 if(MaxLength > 0) //if MaxLength was set greater than zero, array is Static length
48 {
49 tf_size = MaxLength;
50 tf_string = new char[tf_size];
51 tf_isStatic = true;
52 }
53 else //set as Dynamic length larger than starting string
54 {
55 tf_size = strlen(StartString) + 10;
56 tf_string = new char[tf_size];
57 tf_isStatic = false;
58 }
59
60 strcpy(tf_string,StartString);
61 tf_string[strlen(tf_string)] = '\0';
62
63 tf_position = StartPosition;
64 tf_conserveMemory = false;
65}
66
67char * TextField::GetString()
68{
69 return tf_string;
70}
71
72int TextField::GetPosition()
73{
74 return tf_position;
75}
76
77int TextField::GetSize()
78{
79 return tf_size;
80}
81
82bool TextField::IsConserve()
83{
84 return tf_conserveMemory;
85}
86
87bool TextField::IsStatic()
88{
89 return tf_isStatic;
90}
91
92bool TextField::SetPosition(int p)
93{
94 if(p<0)
95 return false;
96 else if (p > strlen(tf_string))
97 return false;
98 else
99 tf_position = p;
100
101 return true;
102}
103
104void TextField::SetConserve(bool conserve)
105{
106 tf_conserveMemory = conserve;
107}
108
109bool TextField::AddCharacter(char c)
110{
111 int currentLength = strlen(tf_string);
112
113 if(tf_position>currentLength) //catches error if position out of bounds
114 tf_position=currentLength;
115
116 if(tf_position<0) //catches error if position out of bounds
117 tf_position=0;
118
119 if(tf_position==tf_size) //if field is full
120 {
121 if(tf_isStatic) //cannot add any characters
122 return false;
123 else //resize dynamic array (adds 10)
124 {
125 tf_size+=10;
126 char* temp = new char [tf_size];
127 strcpy(temp, tf_string);
128 delete [] tf_string;
129 tf_string=temp;
130 }
131 }
132
133 if(tf_position == strlen(tf_string)) //if position is at end of string
134 strcat(tf_string, c);
135 else if(tf_position == 0) //if position is beginning of string
136 {
137 char * temp = new char [tf_size];
138 temp[0] = c;
139 strcpy(tf_string,strcat(temp, tf_string));
140 delete [] temp;
141 }
142 else
143 {
144 char * temp = new char [tf_size];
145 strncpy(temp,tf_string,tf_position);
146 temp[strlen(temp)] = c;
147 for(int i=tf_position; i<strlen(tf_string);i++)
148 temp[i+1]=tf_string[i];
149 delete [] temp;
150 }
151
152 tf_position++;
153 return true;
154}
155
156bool TextField::DeleteCharacter(bool reverse=false)
157{
158 int currentLength = strlen(tf_string);
159
160 if(tf_position < 0) //catches out of range error
161 tf_position=0;
162 if(tf_position > currentLength) //catches out of range error
163 tf_position = currentLength;
164
165 if(tf_position == 0)
166 {
167 if(!reverse) //cursor at start of field and backspace pressed
168 return false;
169 else //delete character at start of field
170 {
171 char * temp = new char [tf_size];
172 for(int i=1; i<currentLength; i++)
173 temp[i-1]=tf_string[i];
174 delete [] tf_string;
175 tf_string = temp;
176 }
177 }
178 else if(tf_position == currentLength)
179 {
180 if(reverse) //cursor at end of field and delete key pressed
181 return false;
182 else //delete character at end of field
183 strncpy(tf_string,tf_string,currentLength-1);
184 }
185 else
186 {
187 char * temp = new char [tf_size];
188 if(!reverse) //delete character before cursor (BACKSPACE)
189 {
190 strncpy(temp,tf_string,tf_position-1);
191 for(int i=tf_position;i<currentLength;i++)
192 temp[i-1]=tf_string[i];
193 tf_position--;
194 }
195 else //delete character after cursor (DELETE)
196 {
197 strncpy(temp,tf_string,tf_position);
198 for(int i=tf_position+1;i<currentLength;i++)
199 temp[i-1]=tf_string[i];
200 }
201 delete [] tf_string;
202 tf_string=temp;
203 }
204
205 if(!tf_isStatic && tf_conserveMemory && tf_size>20 && currentLength+19<=tf_size) //resizes array to conserve memory (removes 10)
206 {
207 tf_size-=10;
208 char* temp = new char [tf_size];
209 strcpy(temp, tf_string);
210 delete [] tf_string;
211 tf_string=temp;
212 }
213
214 return true;
215}
216
217bool TextField::ProcessKey(ALLEGRO_EVENT ev)
218{
219 ALLEGRO_KEYBOARD_STATE *state;
220 al_get_keyboard_state(state);
221
222 if(ev.type == ALLEGRO_EVENT_KEY_DOWN && !al_key_down(state, ALLEGRO_KEY_ALT) && !al_key_down(state, ALLEGRO_KEY_ALTGR) && !al_key_down(state, ALLEGRO_KEY_RCTRL) && !al_key_down(state, ALLEGRO_KEY_LCTRL))
223 {
224 if(ev.keyboard.keycode >= ALLEGRO_KEY_A && ev.keyboard.keycode <= ALLEGRO_KEY_Z)
225 {
226 if(al_key_down(state, ALLEGRO_KEY_LSHIFT) || al_key_down(state, ALLEGRO_KEY_RSHIFT))
227 return AddCharacter('A'+ev.keyboard.keycode-ALLEGRO_KEY_A);
228 else
229 return AddCharacter('a'+ev.keyboard.keycode-ALLEGRO_KEY_A);
230 }
231 else if (ev.keyboard.keycode >= ALLEGRO_KEY_0 && ev.keyboard.keycode <= ALLEGRO_KEY_9)
232 {
233 if(al_key_down(state, ALLEGRO_KEY_LSHIFT) || al_key_down(state, ALLEGRO_KEY_RSHIFT))
234 return AddCharacter('A'+ev.keyboard.keycode-ALLEGRO_KEY_A);
235 else
236 return AddCharacter('a'+ev.keyboard.keycode-ALLEGRO_KEY_A);
237 }
238 else if (ev.keyboard.keycode >= ALLEGRO_KEY_PAD_0 && ev.keyboard.keycode <= ALLEGRO_KEY_PAD_9)
239 {
240 return AddCharacter('0'+ev.keyboard.keycode-ALLEGRO_KEY_PAD_0);
241 }
242 else
243 {
244 switch(ev.keyboard.keycode)
245 {
246 case ALLEGRO_KEY_TILDE :
247 if(al_key_down(state, ALLEGRO_KEY_LSHIFT) || al_key_down(state, ALLEGRO_KEY_RSHIFT))
248 return AddCharacter('~');
249 else
250 return AddCharacter('`');
251 break;
252 case ALLEGRO_KEY_MINUS :
253 if(al_key_down(state, ALLEGRO_KEY_LSHIFT) || al_key_down(state, ALLEGRO_KEY_RSHIFT))
254 return AddCharacter('_');
255 else
256 return AddCharacter('-');
257 break;
258 case ALLEGRO_KEY_EQUALS :
259 if(al_key_down(state, ALLEGRO_KEY_LSHIFT) || al_key_down(state, ALLEGRO_KEY_RSHIFT))
260 return AddCharacter('+');
261 else
262 return AddCharacter('=');
263 break;
264 case ALLEGRO_KEY_BACKSPACE :
265 return DeleteCharacter();
266 break;
267 case ALLEGRO_KEY_OPENBRACE :
268 if(al_key_down(state, ALLEGRO_KEY_LSHIFT) || al_key_down(state, ALLEGRO_KEY_RSHIFT))
269 return AddCharacter('{');
270 else
271 return AddCharacter('[');
272 break;
273 case ALLEGRO_KEY_CLOSEBRACE :
274 if(al_key_down(state, ALLEGRO_KEY_LSHIFT) || al_key_down(state, ALLEGRO_KEY_RSHIFT))
275 return AddCharacter('}');
276 else
277 return AddCharacter(']');
278 break;
279 case ALLEGRO_KEY_SEMICOLON :
280 if(al_key_down(state, ALLEGRO_KEY_LSHIFT) || al_key_down(state, ALLEGRO_KEY_RSHIFT))
281 return AddCharacter(':');
282 else
283 return AddCharacter(';');
284 break;
285 case ALLEGRO_KEY_QUOTE :
286 if(al_key_down(state, ALLEGRO_KEY_LSHIFT) || al_key_down(state, ALLEGRO_KEY_RSHIFT))
287 return AddCharacter('\"');
288 else
289 return AddCharacter('\'');
290 break;
291 case ALLEGRO_KEY_BACKSLASH :
292 if(al_key_down(state, ALLEGRO_KEY_LSHIFT) || al_key_down(state, ALLEGRO_KEY_RSHIFT))
293 return AddCharacter('|');
294 else
295 return AddCharacter('\\');
296 break;
297 case ALLEGRO_KEY_BACKSLASH2 : /* DirectInput calls this DIK_OEM_102: "< > | on UK/Germany keyboards" */
298 if(al_key_down(state, ALLEGRO_KEY_LSHIFT) || al_key_down(state, ALLEGRO_KEY_RSHIFT))
299 return AddCharacter('|');
300 else
301 return AddCharacter('\\');
302 break;
303 case ALLEGRO_KEY_COMMA :
304 if(al_key_down(state, ALLEGRO_KEY_LSHIFT) || al_key_down(state, ALLEGRO_KEY_RSHIFT))
305 return AddCharacter('<');
306 else
307 return AddCharacter(',');
308 break;
309 case ALLEGRO_KEY_FULLSTOP :
310 if(al_key_down(state, ALLEGRO_KEY_LSHIFT) || al_key_down(state, ALLEGRO_KEY_RSHIFT))
311 return AddCharacter('>');
312 else
313 return AddCharacter('.');
314 break;
315 case ALLEGRO_KEY_SLASH :
316 if(al_key_down(state, ALLEGRO_KEY_LSHIFT) || al_key_down(state, ALLEGRO_KEY_RSHIFT))
317 return AddCharacter('?');
318 else
319 return AddCharacter('/');
320 break;
321 case ALLEGRO_KEY_SPACE :
322 return AddCharacter(' ');
323 break;
324
325 case ALLEGRO_KEY_LEFT :
326 if(tf_position > 0)
327 tf_position--;
328 break;
329 case ALLEGRO_KEY_RIGHT :
330 if(tf_position < strlen(tf_string))
331 tf_position++;
332 break;
333 case ALLEGRO_KEY_UP :
334 //if you want to add a keyboard buffer of previous tf_strings
335 break;
336 case ALLEGRO_KEY_DOWN :
337 //same as key up
338 break;
339
340 case ALLEGRO_KEY_PAD_SLASH :
341 return AddCharacter('/');
342 break;
343 case ALLEGRO_KEY_PAD_ASTERISK :
344 return AddCharacter('*');
345 break;
346 case ALLEGRO_KEY_PAD_MINUS :
347 return AddCharacter('-');
348 break;
349 case ALLEGRO_KEY_PAD_PLUS :
350 return AddCharacter('+');
351 break;
352 case ALLEGRO_KEY_PAD_DELETE :
353 return DeleteCharacter(true);
354 break;
355
356 case ALLEGRO_KEY_COLON2 :
357 return AddCharacter(':');
358 break;
359
360 case ALLEGRO_KEY_PAD_EQUALS :
361 return AddCharacter('=');
362 break;
363 case ALLEGRO_KEY_BACKQUOTE :
364 return AddCharacter('\'');
365 break;
366 case ALLEGRO_KEY_SEMICOLON2 :
367 if(al_key_down(state, ALLEGRO_KEY_LSHIFT) || al_key_down(state, ALLEGRO_KEY_RSHIFT))
368 return AddCharacter(':');
369 else
370 return AddCharacter(';');
371 break;
372 }
373 }
374 }
375
376 return NULL;
377}
|
Edgar Reynaldo
Major Reynaldo
May 2007
![]() |
Nazerith said:
1TextField::TextField(char * StartString, int StartPosition, int MaxLength=0)
2{
3 if(MaxLength > 0) //if MaxLength was set greater than zero, array is Static length
4 {
5 tf_size = MaxLength; 6 tf_string = new char[tf_size];
7 tf_isStatic = true;
8 }
9 else //set as Dynamic length larger than starting string
10 {
11 tf_size = strlen(StartString) + 10;
12 tf_string = new char[tf_size];
13 tf_isStatic = false;
14 }
15
18
19 tf_position = StartPosition;
20 tf_conserveMemory = false;
21}
If MaxLength is less than or equal to the number of characters in StartString, then your strcpy call will overrun tf_string. Also, you overwrite the last character of the string with NULL when you use tf_string[strlen(tf_string)] = '\0'; Something else, strcpy writes a null character at the end of the string, so you don't need to do it manually. Nazerith said:
1bool TextField::AddCharacter(char c)
2{
3 int currentLength = strlen(tf_string);
4
5 if(tf_position>currentLength) //catches error if position out of bounds
6 tf_position=currentLength;
7
8 if(tf_position<0) //catches error if position out of bounds
9 tf_position=0;
10
11/// Problem here, this checks the position, not the size
12 13 if(tf_position==tf_size) //if field is full
14 {
15 if(tf_isStatic) //cannot add any characters
16 return false;
17 else //resize dynamic array (adds 10)
18 {
19 tf_size+=10;
20 char* temp = new char [tf_size];
21 strcpy(temp, tf_string);
22 delete [] tf_string;
23 tf_string=temp;
24 }
25 }
26
27 if(tf_position == strlen(tf_string)) //if position is at end of string
29 else if(tf_position == 0) //if position is beginning of string
30 {
31 char * temp = new char [tf_size];
32 temp[0] = c;
33 strcpy(tf_string,strcat(temp, tf_string));
34 delete [] temp;
35 }
36 else
37 {
38 char * temp = new char [tf_size];
39 strncpy(temp,tf_string,tf_position);
40 temp[strlen(temp)] = c;
41 for(int i=tf_position; i<strlen(tf_string);i++)
42 temp[i+1]=tf_string[i];
43 delete [] temp;
44 }
45
46 tf_position++;
47 return true;
48}
c is a character - the second argument of strcat should be a const char*. You're also not checking the size of the string when you add a character to it, so you could overwrite it. Nazerith said: You're asking allegro to store the keyboard state in an uninitialized pointer. It should be : ALLEGRO_KEYBOARD_STATE state; al_get_keyboard_state(&state); You're using C++ - why don't you just use a std::string? It would make manipulation of the string easier, and would manage the memory for you. See also string::erase and string::insert. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
Nazerith
Member #12,551
February 2011
|
Edgar Reynaldo said: If MaxLength is less than or equal to the number of characters in StartString, then your strcpy call will overrun tf_string. Also, you overwrite the last character of the string with NULL when you use tf_string[strlen(tf_string)] = '\0'; Something else, strcpy writes a null character at the end of the string, so you don't need to do it manually.
True, I was half awake when I wrote much of this. But I'm glad for the comments, makes it easier to find errors. I've already noticed a half dozen myself since posting, Edgar Reynaldo said: 1bool TextField::AddCharacter(char c)
2{
3 int currentLength = strlen(tf_string);
4
5 if(tf_position>currentLength) //catches error if position out of bounds
6 tf_position=currentLength;
7
8 if(tf_position<0) //catches error if position out of bounds
9 tf_position=0;
10
11/// Problem here, this checks the position, not the size
12 if(tf_position==tf_size) //if field is full
13 {
14 if(tf_isStatic) //cannot add any characters
15 return false;
16 else //resize dynamic array (adds 10)
17 {
18 tf_size+=10;
19 char* temp = new char [tf_size];
20 strcpy(temp, tf_string);
21 delete [] tf_string;
22 tf_string=temp;
23 }
24 }
25
26 if(tf_position == strlen(tf_string)) //if position is at end of string
27 strcat(tf_string, c);
28 else if(tf_position == 0) //if position is beginning of string
29 {
30 char * temp = new char [tf_size];
31 temp[0] = c;
32 strcpy(tf_string,strcat(temp, tf_string));
33 delete [] temp;
34 }
35 else
36 {
37 char * temp = new char [tf_size];
38 strncpy(temp,tf_string,tf_position);
39 temp[strlen(temp)] = c;
40 for(int i=tf_position; i<strlen(tf_string);i++)
41 temp[i+1]=tf_string[i];
42 delete [] temp;
43 }
44
45 tf_position++;
46 return true;
47}
c is a character - the second argument of strcat should be a const char*. oddly my compiler doesn't seem to mind this, easy enough to fix. Edgar Reynaldo said: You're also not checking the size of the string when you add a character to it, so you could overwrite it. blarg. line 12 in the example should have been: if(tf_size<=currentLength) Less than should be impossible in theory, so I could get away with an == but nothing is really lost by making it <=. Tomorrow after some sleep I'll throw a wrapper around this thing to render it and do some basic testing, work out the kinks. Too sleepy now. Nights. [EDIT] So insomnia got me once again, no sleep. sigh... On the bright side, I build a wrapper to test everything. So here is the next version. I'll call this Alpha (why not?) Changes:
So here is TextField Alpha: TextField.h 1#ifndef TEXTFIELD
2#define TEXTFIELD
3
4#include <string.h>
5
6#include <allegro5\allegro.h>
7
8class TextField
9{
10 char * tf_string; //current string
11 int tf_position; //cursor position
12 int tf_array_size; //size allocated for array
13 int tf_string_size; //size of current string
14 bool tf_isStatic; //defaults to false (dynamic allocation)
15 bool tf_conserveMemory; //if set dynamic arrays are shortened to conserve memory
16
17 bool AddCharacter(char c); //add character at current cursor position. (return true if success)
18 bool DeleteCharacter(bool reverse = false); //delete character at current position. (return true if success)
19
20public:
21 TextField(int maxLength=0); //initialize empty field
22 TextField(char * StartString, int StartPosition, int maxLength=0); //initialize field with starting string and cursor position
23
24 ~TextField(); //destructor, deallocates tf_string;
25
26 char * GetString(); //returns current string
27 int GetPosition(); //returns current cursor position
28 int GetSize(); //returns current size of string
29 int GetArraySize(); //returns current size of array
30 bool IsConserve(); //returns if dynamic arrays are resized to conserve memory
31 bool IsStatic(); //returns if field has static length
32
33 bool SetPosition(int p); //set cursor position
34 void SetConserve(bool conserve); //set dynamic string memory conservation
35 bool SetString(const char* s);
36
37 bool Clear();
38
39 bool ProcessKey(ALLEGRO_EVENT ev); //processes a key press (returns true if success, false if failure, null if ignored)
40};
41
42TextField::TextField(int maxLength)
43{
44 if(maxLength > 0) //if MaxLength was set greater than zero, array is Static length
45 {
46 tf_array_size = maxLength;
47 tf_isStatic = true;
48 }
49 else //set as dynamic with a starting 10 character storage
50 {
51 tf_array_size = 10;
52 tf_isStatic = false;
53 }
54 tf_string = new char[tf_array_size];
55 tf_string[0] = '\0';
56 tf_string_size = 0;
57 tf_position = 0;
58 tf_conserveMemory = false;
59}
60
61TextField::TextField(char * StartString, int StartPosition, int MaxLength)
62{
63 tf_string_size = strlen(StartString);
64
65 if(MaxLength > 0) //if MaxLength was set greater than zero, array is Static length
66 {
67 tf_array_size = MaxLength;
68 tf_string = new char[tf_array_size];
69 tf_isStatic = true;
70 if(tf_string_size > tf_array_size)
71 {
72 tf_string_size = tf_array_size;
73 strncpy(tf_string,StartString,tf_array_size);
74 }
75 else
76 strcpy(tf_string,StartString);
77 }
78 else //set as Dynamic length larger than starting string
79 {
80 tf_array_size = tf_string_size + 10;
81 tf_string = new char[tf_array_size];
82 tf_isStatic = false;
83 strcpy(tf_string,StartString);
84 }
85
86 if(StartPosition<0)
87 tf_position=0;
88 else if(StartPosition>tf_string_size)
89 tf_position=tf_string_size;
90 else
91 tf_position = StartPosition;
92 tf_conserveMemory = false;
93}
94
95TextField::~TextField()
96{
97 delete [] tf_string;
98}
99
100char * TextField::GetString()
101{
102 return tf_string;
103}
104
105int TextField::GetPosition()
106{
107 return tf_position;
108}
109
110int TextField::GetSize()
111{
112 return tf_string_size;
113}
114
115int TextField::GetArraySize()
116{
117 return tf_array_size;
118}
119
120bool TextField::IsConserve()
121{
122 return tf_conserveMemory;
123}
124
125bool TextField::IsStatic()
126{
127 return tf_isStatic;
128}
129
130bool TextField::SetPosition(int p)
131{
132 if(p<0)
133 return false;
134 else if (p > tf_string_size)
135 return false;
136 else
137 tf_position = p;
138
139 return true;
140}
141
142void TextField::SetConserve(bool conserve)
143{
144 tf_conserveMemory = conserve;
145}
146
147bool TextField::SetString(const char* NewString)
148{
149 tf_string_size = strlen(NewString);
150
151 if(tf_isStatic)
152 {
153 if(tf_string_size > tf_array_size)
154 {
155 tf_string_size = tf_array_size;
156 strncpy(tf_string,NewString,tf_array_size);
157 }
158 else
159 strcpy(tf_string,NewString);
160 }
161 else //set as Dynamic length larger than starting string
162 {
163 tf_array_size = tf_string_size + 10;
164 delete [] tf_string;
165 tf_string = new char[tf_array_size];
166 strcpy(tf_string,NewString);
167 }
168
169 if(tf_position<0)
170 tf_position=0;
171 else if(tf_position>tf_string_size)
172 tf_position=tf_string_size;
173
174 return(true);
175}
176
177bool TextField::Clear()
178{
179
180 if(!tf_isStatic)
181 tf_array_size = 10;
182
183 tf_string[0] = '\0';
184 tf_string_size = 0;
185 tf_position = 0;
186
187 return(true);
188}
189
190bool TextField::AddCharacter(char c)
191{
192 if(tf_position>tf_string_size) //catches error if position out of bounds
193 tf_position=tf_string_size;
194
195 if(tf_position<0) //catches error if position out of bounds
196 tf_position=0;
197
198 if(tf_array_size<=tf_string_size+1) //if field is full
199 {
200 if(tf_isStatic) //cannot add any characters
201 return false;
202 else //resize dynamic array (adds 10)
203 {
204 tf_array_size+=10;
205 char* temp = new char [tf_array_size];
206 strcpy(temp, tf_string);
207 delete [] tf_string;
208 tf_string=temp;
209 }
210 }
211
212 memmove(tf_string+tf_position+1, tf_string+tf_position, tf_string_size-tf_position+1);
213 tf_string[tf_position] = c;
214 tf_position++;
215 tf_string_size++;
216
217 return true;
218}
219
220bool TextField::DeleteCharacter(bool reverse)
221{
222 if(tf_position < 0) //catches out of range error
223 tf_position=0;
224 if(tf_position > tf_string_size) //catches out of range error
225 tf_position = tf_string_size;
226
227 if(reverse)
228 {
229 if(tf_position==tf_string_size)
230 return false;
231 else
232 memmove(tf_string+tf_position, tf_string+tf_position+1, tf_string_size-tf_position);
233 }
234 else
235 {
236 if(tf_position==0)
237 return false;
238 else
239 {
240 memmove(tf_string+tf_position-1, tf_string+tf_position, tf_string_size-tf_position+1);
241 tf_position--;
242 }
243 }
244
245 tf_string_size--;
246
247 if(!tf_isStatic && tf_conserveMemory && tf_array_size>=20 && tf_string_size+19<=tf_array_size) //resizes array to conserve memory (removes 10)
248 {
249 tf_array_size-=10;
250 char* temp = new char [tf_array_size];
251 strcpy(temp, tf_string);
252 delete [] tf_string;
253 tf_string=temp;
254 }
255
256 return true;
257}
258
259bool TextField::ProcessKey(ALLEGRO_EVENT ev)
260{
261 ALLEGRO_KEYBOARD_STATE state;
262 bool shiftDown=false;
263
264 al_get_keyboard_state(&state);
265
266 if(ev.type != ALLEGRO_EVENT_KEY_DOWN ||
267 al_key_down(&state, ALLEGRO_KEY_ALT) || al_key_down(&state, ALLEGRO_KEY_ALTGR) ||
268 al_key_down(&state, ALLEGRO_KEY_RCTRL) || al_key_down(&state, ALLEGRO_KEY_LCTRL))
269 return NULL;
270 else
271 {
272 if(al_key_down(&state, ALLEGRO_KEY_LSHIFT) || al_key_down(&state, ALLEGRO_KEY_RSHIFT))
273 shiftDown=true;
274
275 if(ev.keyboard.keycode >= ALLEGRO_KEY_A && ev.keyboard.keycode <= ALLEGRO_KEY_Z)
276 {
277 if(shiftDown)
278 return AddCharacter('A'+ev.keyboard.keycode-ALLEGRO_KEY_A);
279 else
280 return AddCharacter('a'+ev.keyboard.keycode-ALLEGRO_KEY_A);
281 }
282 else if (ev.keyboard.keycode >= ALLEGRO_KEY_0 && ev.keyboard.keycode <= ALLEGRO_KEY_9)
283 {
284 if(shiftDown)
285 switch(ev.keyboard.keycode)
286 {
287 case ALLEGRO_KEY_0 :
288 AddCharacter(')');
289 break;
290 case ALLEGRO_KEY_1 :
291 AddCharacter('!');
292 break;
293 case ALLEGRO_KEY_2 :
294 AddCharacter('@');
295 break;
296 case ALLEGRO_KEY_3 :
297 AddCharacter('#');
298 break;
299 case ALLEGRO_KEY_4 :
300 AddCharacter('$');
301 break;
302 case ALLEGRO_KEY_5 :
303 AddCharacter('%');
304 break;
305 case ALLEGRO_KEY_6 :
306 AddCharacter('^');
307 break;
308 case ALLEGRO_KEY_7 :
309 AddCharacter('&');
310 break;
311 case ALLEGRO_KEY_8 :
312 AddCharacter('*');
313 break;
314 case ALLEGRO_KEY_9 :
315 AddCharacter('(');
316 break;
317 }
318 else
319 return AddCharacter('0'+ev.keyboard.keycode-ALLEGRO_KEY_0);
320 }
321 else if (ev.keyboard.keycode >= ALLEGRO_KEY_PAD_0 && ev.keyboard.keycode <= ALLEGRO_KEY_PAD_9)
322 {
323 return AddCharacter('0'+ev.keyboard.keycode-ALLEGRO_KEY_PAD_0);
324 }
325 else
326 {
327 switch(ev.keyboard.keycode)
328 {
329 case ALLEGRO_KEY_TILDE :
330 if(shiftDown)
331 return AddCharacter('~');
332 else
333 return AddCharacter('`');
334 break;
335 case ALLEGRO_KEY_MINUS :
336 if(shiftDown)
337 return AddCharacter('_');
338 else
339 return AddCharacter('-');
340 break;
341 case ALLEGRO_KEY_EQUALS :
342 if(shiftDown)
343 return AddCharacter('+');
344 else
345 return AddCharacter('=');
346 break;
347 case ALLEGRO_KEY_BACKSPACE :
348 return DeleteCharacter();
349 break;
350 case ALLEGRO_KEY_OPENBRACE :
351 if(shiftDown)
352 return AddCharacter('{');
353 else
354 return AddCharacter('[');
355 break;
356 case ALLEGRO_KEY_CLOSEBRACE :
357 if(shiftDown)
358 return AddCharacter('}');
359 else
360 return AddCharacter(']');
361 break;
362 case ALLEGRO_KEY_SEMICOLON :
363 if(shiftDown)
364 return AddCharacter(':');
365 else
366 return AddCharacter(';');
367 break;
368 case ALLEGRO_KEY_QUOTE :
369 if(shiftDown)
370 return AddCharacter('\"');
371 else
372 return AddCharacter('\'');
373 break;
374 case ALLEGRO_KEY_BACKSLASH :
375 if(shiftDown)
376 return AddCharacter('|');
377 else
378 return AddCharacter('\\');
379 break;
380 case ALLEGRO_KEY_BACKSLASH2 : /* DirectInput calls this DIK_OEM_102: "< > | on UK/Germany keyboards" */
381 if(shiftDown)
382 return AddCharacter('|');
383 else
384 return AddCharacter('\\');
385 break;
386 case ALLEGRO_KEY_COMMA :
387 if(shiftDown)
388 return AddCharacter('<');
389 else
390 return AddCharacter(',');
391 break;
392 case ALLEGRO_KEY_FULLSTOP :
393 if(shiftDown)
394 return AddCharacter('>');
395 else
396 return AddCharacter('.');
397 break;
398 case ALLEGRO_KEY_SLASH :
399 if(shiftDown)
400 return AddCharacter('?');
401 else
402 return AddCharacter('/');
403 break;
404 case ALLEGRO_KEY_SPACE :
405 return AddCharacter(' ');
406 break;
407
408 case ALLEGRO_KEY_DELETE :
409 return DeleteCharacter(true);
410 break;
411 case ALLEGRO_KEY_HOME :
412 tf_position = 0;
413 break;
414 case ALLEGRO_KEY_END :
415 tf_position = tf_string_size;
416 break;
417 case ALLEGRO_KEY_LEFT :
418 if(tf_position > 0)
419 tf_position--;
420 break;
421 case ALLEGRO_KEY_RIGHT :
422 if(tf_position < tf_string_size)
423 tf_position++;
424 break;
425 case ALLEGRO_KEY_UP :
426 //if you want to add a keyboard buffer of previous tf_strings
427 break;
428 case ALLEGRO_KEY_DOWN :
429 //same as key up
430 break;
431
432 case ALLEGRO_KEY_PAD_SLASH :
433 return AddCharacter('/');
434 break;
435 case ALLEGRO_KEY_PAD_ASTERISK :
436 return AddCharacter('*');
437 break;
438 case ALLEGRO_KEY_PAD_MINUS :
439 return AddCharacter('-');
440 break;
441 case ALLEGRO_KEY_PAD_PLUS :
442 return AddCharacter('+');
443 break;
444 case ALLEGRO_KEY_PAD_DELETE :
445 return DeleteCharacter(true);
446 break;
447
448 case ALLEGRO_KEY_COLON2 :
449 return AddCharacter(':');
450 break;
451
452 case ALLEGRO_KEY_PAD_EQUALS :
453 return AddCharacter('=');
454 break;
455 case ALLEGRO_KEY_BACKQUOTE :
456 return AddCharacter('\'');
457 break;
458 case ALLEGRO_KEY_SEMICOLON2 :
459 if(shiftDown)
460 return AddCharacter(':');
461 else
462 return AddCharacter(';');
463 break;
464 }
465 }
466 }
467
468 return NULL;
469}
470
471
472#endif
|
Edgar Reynaldo
Major Reynaldo
May 2007
![]() |
Nazerith said: speed is why I'm not using std::String This may be counter intuitive, but you'll probably save more time in development by using std::string. And also, the standard library has probably been optimized better than you could do with char* yourself. There's no way that at 60 updates per second that you will see any difference in speed of any kind by using char*'s manually instead of using std::string. Nazerith said:
tf_array_size should be MaxLength plus one, to account for the null terminator. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
Peter Wang
Member #23
April 2000
|
Ugh. bool TextField::ProcessKey(ALLEGRO_EVENT ev) { ALLEGRO_KEYBOARD_STATE state; bool shiftDown=false; al_get_keyboard_state(&state); if(ev.type != ALLEGRO_EVENT_KEY_DOWN || al_key_down(&state, ALLEGRO_KEY_ALT) || al_key_down(&state, ALLEGRO_KEY_ALTGR) || al_key_down(&state, ALLEGRO_KEY_RCTRL) || al_key_down(&state, ALLEGRO_KEY_LCTRL)) return NULL; else ev.keyboard.modifiers already has this information. if(ev.keyboard.keycode >= ALLEGRO_KEY_A && ev.keyboard.keycode <= ALLEGRO_KEY_Z) { if(shiftDown) return AddCharacter('A'+ev.keyboard.keycode-ALLEGRO_KEY_A); else return AddCharacter('a'+ev.keyboard.keycode-ALLEGRO_KEY_A); } The ev.keyboard.unichar field already has the information you need. Your code is needlessly verbose and will only "work" on US keyboard layouts. If something is very repetitious, alarms should be sounding off in your head. Programmers hate repetition.
|
Nazerith
Member #12,551
February 2011
|
Edgar Reynaldo said: This may be counter intuitive, but you'll probably save more time in development by using std::string. And also, the standard library has probably been optimized better than you could do with char* yourself. There's no way that at 60 updates per second that you will see any difference in speed of any kind by using char*'s manually instead of using std::string. Could I create a general purpose string library thats quicker than std:string. most likely not. But in the very specific usage I'm implementing here, its already faster than std:string for various reasons. 1) Every time you concatenate or lengthen a std:string, new memory is allocated. I'm only reallocating at a minimum of every ten concatenations. std:string has a bit of overhead that makes it very flexible. I don't need any of that flexibility, so I've sacrificed the the overhead. You can find some places on the internet where people have run comparison tests. Depending on the exact test, char* operates roughly 2x to 10x faster than std::string but you have to be willing to handle your own memory management. I'm not worried about handling it 60 times a minute, but in a game there are frequently lots of events between frame renders. The less time it spends processing here, the more time it has to do other things. Since I'm working up these components for an online game, I expect lots of other things happening as well. Edgar Reynaldo said:
tf_array_size = MaxLength; //... strncpy(tf_string,StartString,tf_array_size);
tf_array_size should be MaxLength plus one, to account for the null terminator. I've been testing dynamic strings mostly. Guess I'll need to make the array for fixed strings one larger. However on the strncpy I don't need to account for the null character in the length, since the function adds a null at the end anyways (as you pointed out previously). Peter Wang said: ev.keyboard.modifiers already has this information. Yep. Originally I was using the Key Down event for my class, but have already switched to Key Char events. So in my next version I've eliminated the need to test state (which should get rid of any timing errors for rapid key presses), and it also lets me handle when someone holds down a key. Peter Wang said: The ev.keyboard.unichar field already has the information you need. Your code is needlessly verbose and will only "work" on US keyboard layouts. If something is very repetitious, alarms should be sounding off in your head. Programmers hate repetition. I'm still learning all the things in the Allegro library. I'll take a look at the unicode stuff and see about implementing it. Thanks for the feedback guys. Til Laters. |
Edgar Reynaldo
Major Reynaldo
May 2007
![]() |
Nazerith said: I've been testing dynamic strings mostly. Guess I'll need to make the array for fixed strings one larger. However on the strncpy I don't need to account for the null character in the length, since the function adds a null at the end anyways (as you pointed out previously). strcpy adds a null, if there is space for it, but I can't say for sure that strncpy does the same thing. And there's still the problem of using strncpy to copy X elements into an array that only has X elements. If it does add a trailing null, there will still be an overflow. If it does not, you still have a string that is not null terminated. My Website! | EAGLE GUI Library Demos | My Deviant Art Gallery | Spiraloid Preview | A4 FontMaker | Skyline! (Missile Defense) Eagle and Allegro 5 binaries | Older Allegro 4 and 5 binaries | Allegro 5 compile guide |
Nazerith
Member #12,551
February 2011
|
Agreed. Just to play it safe I'll make the array one larger. |
jmasterx
Member #11,410
October 2009
|
Mine uses Unichar and uses UTF-8 and std::string, much cleaner & more effective imho. Agui GUI API -> https://github.com/jmasterx/Agui |
Nazerith
Member #12,551
February 2011
|
Unicode does simply down the code, but now its too simplified. Allegro API Docs said:
keyboard.unichar (int) So a simple test of "ev.keyboard.unichar > 0" should do right? Well it does for arrow keys and delete, but not for esc, backspace and a number of other keys that are "non-visible". So now I have the opposite issue...I've got to test for every key that doesn't belong. While obviously easier in the long run (if you are shooting for international keyboards), its still a pain. Urg. For those out their who have messed with the unicode field before, you have a simple way to filter out the garbage? |
jmasterx
Member #11,410
October 2009
|
1void AguiTextBox::handleKeyboard( const AguiKeyEventArgs &keyArgs )
2{
3
4 forceShowCaret();
5 resetCaretBlinkTime();
6 if(keyArgs.getExtendedKey() == AGUI_EXT_KEY_UP)
7 {
8 keyPositionCaret(getCaretColumn(),getCaretRow() - 1);
9 return;
10 }
11 else if(keyArgs.getExtendedKey() == AGUI_EXT_KEY_DOWN)
12 {
13 keyPositionCaret(getCaretColumn(),getCaretRow() + 1);
14 return;
15 }
16 else if(keyArgs.getExtendedKey() == AGUI_EXT_KEY_LEFT)
17 {
18 keyPositionCaret(getCaretColumn() - 1,getCaretRow());
19 return;
20 }
21 else if(keyArgs.getExtendedKey() == AGUI_EXT_KEY_RIGHT)
22 {
23 keyPositionCaret(getCaretColumn() + 1,getCaretRow());
24 return;
25 }
26 else if(keyArgs.getKey() == AGUI_KEY_BACKSPACE)
27 {
28 removeLastCharacter();
29 }
30 else if(keyArgs.getKey() == AGUI_KEY_DELETE)
31 {
32 removeNextCharacter();
33 }
34 else if(keyArgs.getKey() == AGUI_KEY_ENTER)
35 {
36 addToNextCharacter('\n');
37 }
38 else if(keyArgs.getUnichar() >= ' ')
39 {
40 addToNextCharacter(keyArgs.getUnichar());
41 }
42
43
44}
Agui GUI API -> https://github.com/jmasterx/Agui |
Tobias Dammers
Member #2,604
August 2002
![]() |
Nazerith said:
1) Every time you concatenate or lengthen a std:string, new memory is allocated. I'm only reallocating at a minimum of every ten concatenations.
Those are assumptions you're making, and they're both wrong. --- |
jmasterx
Member #11,410
October 2009
|
Since it uses a std::vector you should also be able to reserve x, that way if you know about how much you will need you can preallocate it. Agui GUI API -> https://github.com/jmasterx/Agui |
|
1
2
|