Text input.
julian_boolean

Just wondering how everyone here deals with inputting text to the screen when your using graphics.

I was trying to use gstream but it didn't seem to work very well.. Whenever I tried normal output the characters would just flash as if they weren't being buffered properly, then when I tried input, it would just be a black screen until I mashed a bunch of keys then my graphics would show. The characters always went over my mouse too no matter where I put the functions.

Before gstream, I was looking at this: http://www.gamedev.net/reference/articles/article2130.asp

It seemed to work pretty good, but I was just curious to see other ideas.

Steve Terry

I don't see any better way of handling text input other than using keypressed and the scancode/ascii code of the key pressed. The methods in the article are correct, if you are seeing text written over your cursor than that is a problem with how you are drawing things to the screen, draw text, then cursor, not the other way around.

julian_boolean

This guy's example is a little difficult for me to use. What I was trying to do with it was make it into a class, then use it in my game state classes like this:

1titlescreen::logic(BITMAP *textbuffer);
2{
3 cText name;
4 
5 name.key_input(textBuffer);
6 name.set_coords(100,100); // <-- hopefully
7 
8 cText pass;
9 
10 pass.key_input(textBuffer);
11 pass.set_coords(100,100); // <-- hopefully
12}
13 
14titlescreen::draw(BITMAP *buffer);
15{
16 // various graphics
17}

But I think the buffers are kinda conflicting and it's making the entire screen flash.. Even if I could get this to work I have no idea how I'd be able to choose and input text between "name" and "pass" with the mouse.

Steve Terry

Why would you create a new cText object each time you called logic that makes no sense. Just make a name and pass object in your constructor, then in your logic you can do an if(needs_login){ name.set_coords(100, 100); pass.set_coords(100, 150); } Your class should get input only when needed so I'm guessing set_coords would activate the input box or you could have a separate activate object. When the user clicks OK or whatever then you call name.get_text() and pass.get_text() which will return the buffers in those classes.

julian_boolean

Not really sure what you mean.. I had always created different instances of interface stuff if I wanted them to have different properties and do different things.

I also managed to sorta get the text to stop flashing by putting it in with my draw function.. Which kinda annoys me because basically everything has to be in the draw function since its the only one thats blitting the buffer. If i try to blit the buffer in a different function then try to run it through the game loop it'll flicker again, ugh.

Steve Terry

What I am trying to say is that you should not have your text input functions stop the program and loop within, instead your functions should simply check for a keypress, if so then add or subtract a character and blit to the buffer, otherwise blit to the buffer the text and return.

julian_boolean

I think I know what your trying to get at now, goina try it out. Also, does anyone know how I could go about setting the capacity or coordinates of the text? Was looking at some Allegro functions for text, but I'm 95% sure this ISN'T how they're suppose to be used, because it's not working. :P

void text::set_coords(int w, int h)
{
  w = text_length(font, edittext.c_str());
  h = text_height(font);
}

No idea how I make a function for capacity though.

Steve Terry

void text::set_coords(int &w, int &h)

{
  w = text_length(font, edittext.c_str());
  h = text_height(font);
}

Hmm that would be a get_coords function anyway and it's not coords but bounds.

Johan Halmén

Allegro GUI. d_edit_proc() to be more specific. You define the max length, placement of the text box, font colours etc. You have the backspace functionality, and delete, I think. And you can customise it. I made a customised thing that works with non-even coloured fonts. My Christmashack entry uses it for name input in the score list. Check there how it works.

Does it echo here? Feels like a dejavu.

julian_boolean

Thanks for the help guys. :) NOW I'm trying to combine a button class and the text code into one class on its own. I have the text code in a seperate function (and the code for drawing the text inside the draw function for the button/textfield.)

1#define SELECTED 0
2#define NOT_SELECTED 1
3 
4 ...
5 
6 selection status;
7 
8----------------------------------
9 
10text::init( ... )
11{
12 ...
13 
14 selection_status = NOT_SELECTED;
15}
16 
17text::text_logic()
18{
19 if(selection_status == SELECTED)
20 {
21 while(keypressed())
22 {
23 int newkey = readkey();
24 char ASCII = newkey & 0xff;
25 char scancode = newkey >> 8;
26 ...
27}

So! Inside the logic function for the new/converted button class called text, I had it set selection_status to SELECTED only if the textfield is pressed. For some reason, if I mash a bunch of keys THEN press the textfield, even though it's set to NOT_SELECTED (yet), all the keys that I pressed will appear.

Jeff Bernard

Sounds like you just need to clear the keyboard buffer.

clear_keybuf() before readkey() and prossibly before keypressed() too.

julian_boolean

I went with "else if(selection_status == NOT_SELECTED) { clear_keybuf(); }" and it seems to work sorta okay. Now I want it so, when you click the text field it sets the status to selected and keeps it that way until I say otherwise! Because right now I have to hold in the text field with my mouse to type, then when I let go it stops me from polling the text. For the record I'm using the code for the Loomsoft Button (which can be found here: http://www.loomsoft.net/util_lsbutton.shtml)

Here's what I'm working with now:

#SelectExpand
1#include "text.h" 2#define WHITE makecol(255, 255, 255) 3#include <allegro.h> 4#include <string> 5using namespace std; 6 7string edittext; 8string::iterator iter = edittext.begin(); 9int caret = 0; 10bool insert = true; 11 12void text::initialize(BITMAP *bitmap_on, BITMAP *bitmap_down, BITMAP *bitmap_off, int MOUSE_DETECTION_MODE, int MOUSE_CLICK_MODE) 13{ 14 // Set the bitmap pointers 15 on_bitmap = bitmap_on; 16 down_bitmap = bitmap_down; 17 off_bitmap = bitmap_off; 18 19 // Set all the modes that were passed. 20 mouse_detection_mode = MOUSE_DETECTION_MODE; 21 mouse_status = BUTTON_NOT_ON_FOCUS; 22 mouse_click_mode = MOUSE_CLICK_MODE; 23 24 // Set the bounding box to be the dimensions of the off_bitmap. 25 bound_left = 0; 26 bound_top = 0; 27 bound_right = off_bitmap->w; 28 bound_bottom = off_bitmap->h; 29 30 // Set the x/y position to 0,0 31 x_position = 0; 32 y_position = 0; 33 34 // Set the masking color to the default "magic pink". 35 masking_color = makecol(255,0,255); 36 37 // Turn off the bounding box display feature 38 show_bbox = FALSE; 39 40 // Set the original frame to draw the button to off_bitmap 41 draw_frame = BUTTON_DRAW_FRAME_OFF; 42 43 // Set the button to an active state. 44 is_active = TRUE; 45 46 selection_status = BUTTON_NOT_SELECTED; 47} 48 49void text::set_bounds(int left_bound, int right_bound, int top_bound, int bottom_bound) 50{ 51 if(mouse_detection_mode == BUTTON_BOUNDING_BOX) 52 { 53 bound_left = left_bound; 54 bound_right = right_bound; 55 bound_top = top_bound; 56 bound_bottom = bottom_bound; 57 } 58} 59 60void text::set_masking_color(int red, int blue, int green) 61{ 62 if(red < 0) 63 red = 0; 64 else if(red > 255) 65 red = 255; 66 67 if(blue < 0) 68 blue = 0; 69 else if(blue > 255) 70 blue = 255; 71 72 if(green < 0) 73 green = 0; 74 else if(green > 255) 75 green = 255; 76 77 masking_color = makecol(red,blue,green); 78} 79 80//Sets if the drawing function should show the bounding box. 81void text::set_show_bounding_box(int value) 82{ 83 show_bbox = value; 84} 85 86//Sets the active state for the button 87void text::set_active(int value) 88{ 89 is_active = value; 90} 91 92void text::set_coords(int x, int y) 93{ 94 x_position = x; 95 y_position = y; 96} 97 98void text::poll() 99{ 100 if(mouse_x > bound_left+x_position && mouse_x < bound_right+x_position && 101 mouse_y > bound_top+y_position && mouse_y < bound_bottom+y_position) 102 { 103 if(is_active && selection_status == BUTTON_SELECTED) 104 { 105 while(keypressed()) 106 { 107 int newkey = readkey(); 108 char ASCII = newkey & 0xff; 109 char scancode = newkey >> 8; 110 111 if(ASCII >= 32 && ASCII <= 126) 112 { 113 if(insert || iter == edittext.end()) 114 iter = edittext.insert(iter, ASCII); 115 116 else 117 edittext.replace(caret, 1, 1, ASCII); 118 caret++; 119 iter++; 120 } 121 122 else 123 switch(scancode) 124 { 125 case KEY_DEL: 126 if(iter != edittext.end()) iter = edittext.erase(iter); 127 break; 128 129 case KEY_BACKSPACE: 130 if(iter != edittext.begin()) 131 { 132 caret--; 133 iter--; 134 iter = edittext.erase(iter); 135 }break; 136 137 case KEY_RIGHT: 138 if(iter != edittext.end()) caret++, iter++; 139 break; 140 141 case KEY_LEFT: 142 if(iter != edittext.begin()) caret--, iter--; 143 break; 144 145 case KEY_INSERT: 146 insert = !insert; 147 break; 148 149 default: 150 break; 151 } 152 } 153 } 154 155 else if(selection_status == BUTTON_NOT_SELECTED) { clear_keybuf(); } 156 157 if(mouse_detection_mode == BUTTON_PIXEL_PERFECT) 158 { 159 160 if(getpixel(off_bitmap, mouse_x-x_position,mouse_y-y_position) != makecol(255,0,255)) 161 { 162 if(mouse_b & 1) 163 { 164 selection_status = BUTTON_SELECTED; 165 draw_frame = BUTTON_DRAW_FRAME_DOWN; 166 } 167 168 else 169 { 170 selection_status = BUTTON_NOT_SELECTED; 171 draw_frame = BUTTON_DRAW_FRAME_ON; 172 } 173 } 174 175 else 176 { 177 selection_status = BUTTON_NOT_SELECTED; 178 draw_frame = BUTTON_DRAW_FRAME_OFF; 179 180 if(mouse_status != BUTTON_MOUSE_STATUS_DOWN) 181 { 182 mouse_status = BUTTON_NOT_ON_FOCUS; 183 } 184 } 185 } 186 187 else 188 { 189 if(mouse_b & 1) 190 { 191 draw_frame = BUTTON_DRAW_FRAME_DOWN; 192 } 193 194 else 195 { 196 draw_frame = BUTTON_DRAW_FRAME_ON; 197 } 198 } 199 } 200 201 else 202 { 203 draw_frame = BUTTON_DRAW_FRAME_OFF; 204 205 if(mouse_status != BUTTON_MOUSE_STATUS_DOWN) 206 { 207 mouse_status = BUTTON_NOT_ON_FOCUS; 208 } 209 } 210} 211 212int text::is_clicked() 213{ 214 if(mouse_x > (bound_left+x_position) && mouse_x < (bound_right+x_position) && 215 mouse_y > (bound_top+y_position) && mouse_y < (bound_bottom+y_position)) 216 { 217 if(!(mouse_b & 1) && mouse_status == BUTTON_NOT_ON_FOCUS) 218 { 219 mouse_status = BUTTON_MOUSE_STATUS_UP; 220 } 221 222 if(mouse_b & 1 && mouse_click_mode == BUTTON_CLICK_MODE_CONSTANT) 223 { 224 return TRUE; 225 } 226 227 if(mouse_b & 1 && mouse_status != BUTTON_NOT_ON_FOCUS) 228 { 229 mouse_status = BUTTON_MOUSE_STATUS_DOWN; 230 } 231 232 else if(!(mouse_b & 1) && mouse_status == BUTTON_MOUSE_STATUS_DOWN) 233 { 234 mouse_status = BUTTON_MOUSE_STATUS_UP; 235 236 if(mouse_detection_mode == BUTTON_PIXEL_PERFECT) 237 { 238 if(getpixel(off_bitmap, mouse_x-x_position,mouse_y-y_position) != masking_color) 239 { 240 return TRUE; 241 } 242 } 243 244 else 245 { 246 return TRUE; 247 } 248 } 249 } 250 251 else if(!(mouse_b & 1)) 252 { 253 mouse_status = BUTTON_NOT_ON_FOCUS; 254 } 255 256 return FALSE; 257} 258 259void text::draw_button(BITMAP *bitmap_buffer) 260{ 261 if(draw_frame == BUTTON_DRAW_FRAME_DOWN) 262 { 263 if(down_bitmap != NULL) 264 { 265 acquire_screen(); 266 draw_sprite(bitmap_buffer, down_bitmap, x_position, y_position); 267 release_screen(); 268 vline(bitmap_buffer, caret* 8, 8, 18, WHITE); 269 } 270 } 271 272 else if(draw_frame == BUTTON_DRAW_FRAME_ON) 273 { 274 if(on_bitmap != NULL) 275 { 276 acquire_screen(); 277 draw_sprite(bitmap_buffer, on_bitmap, x_position, y_position); 278 release_screen(); 279 } 280 } 281 282 else if(draw_frame == BUTTON_DRAW_FRAME_OFF) 283 { 284 if(off_bitmap != NULL) 285 { 286 acquire_screen(); 287 draw_sprite(bitmap_buffer, off_bitmap, x_position, y_position); 288 release_screen(); 289 } 290 } 291 292 if(show_bbox) 293 { 294 rect(bitmap_buffer, bound_left+x_position, bound_top+y_position, bound_right+x_position, bound_bottom+y_position, makecol(255,0,0)); 295 } 296 297 textout_ex(bitmap_buffer, font, edittext.c_str(), 0, 10, WHITE, -1); 298}

The string edittext stuff is put right under where I include allegro, the header, etc. I tried to put it somewhere else though it doesn't seem to like it when I do that. Which is sorta a problem because it's always storing the information in the same things.

Steve Terry

Nice globals ;D

You just need an active flag which simply is turned on when you move the mouse inside the bounds of the box and click. The only way it will not be active is if you loose focus to that edit box and into another. For that purpose you can have a text manager which stores a list of all edit boxes you have active at any given time so that when one gains focus you can deactivate the other one. You can even capture TAB as a key to toggle through your list of text objects.

if(green < 0)
    green = 0;
  else if(green > 255)
    green = 255;

You can use MID for this purpose, MID(0, green, 255) :)

23yrold3yrold
Quote:

Just wondering how everyone here deals with inputting text to the screen when your using graphics.

Probably past this point for you, but I wrote this a long time ago and maybe it's worth something ...

Clickies.

julian_boolean

Steve:

I thought that's what I already have. I was planning on having somewhere in my logic function for the game states that if a text field is clicked, it'll stop polling any of the other ones on the screen. Actually I did try to do that but the program would just crash.. It was something simple like:

if(firstName.is_clicked())
{
  lastName.is_active(false)
}

23yrold3yrold:

I'm already using your code ;) works nicely, but I'd still like to put the iter/caret/edittext stuff inside a function if I could find a way how.

Thread #589667. Printed from Allegro.cc