Jump to content
  • Advertisement
Sign in to follow this  
DvDmanDT

Text input?

This topic is 4845 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello everyone.. I'm writing a game, an RTS to be specific.. Now, I was wondering how to do text input.. Or rather, how you'd do it.. I need it for menus (your have to write a name, and perhaps an IP to connect to in network games), in-game chat and a game console (So that clients might get to controll the server for example).. Now, we're using DirectInput, with buffers, for everything, and we're writing the GUI ourselves.. So, I was thinking about marking the input field as active, and then just send all key presses/releases to it, and let it decide what to do.. But I was also thinking that there might already be some nice easy to use class to help me out.. Any suggestions?

Share this post


Link to post
Share on other sites
Advertisement
So how do I do it myself then?

Last time I did it, I made my own linked list, which contained a char and next/prev pointers.. Then in my text_input class, I had three ll pointers: first, last and active.. Then when the user pressed arrow keys, and/or home/end keys I moved around that active one, and on keypress (for the chars that is), I inserted that char before that active node.. I have a slight feeling that it's not the best way to do it.. How would you do it?

Share this post


Link to post
Share on other sites
Let me pull out a lil code snipet I made...


#include <conio.h>
#include <iostream>
using namespace std;

typedef struct charlink
{
unsigned char data;
charlink* next;
}charlinkthing;

unsigned char* input()
{
int s;
int size=0;
unsigned char ch;
unsigned char *string=NULL;
unsigned char *chstr=NULL;
bool done=false;
bool notrue=false;
charlinkthing* start=NULL;
charlinkthing* current=NULL;
charlinkthing* nextcur=NULL;

do
{
ch=getch();
if(ch==13)
{
cout << endl;
s=size;
current->next = NULL;
current=start;
chstr = new unsigned char[size+1];
string = chstr;
do
{
*chstr++=current->data;
nextcur=current;
current=nextcur->next;
nextcur=NULL;
s--;
}while(s);
*chstr='\0';
current=start->next;
delete start;
start=NULL;
do
{
if(current->next=NULL)
notrue=true;
else
{
nextcur=current->next;
delete current;
current = NULL;
}
}while(notrue);
done=true;
}
else
{
cout << ch;
if(!start)
{
current = new charlink;
current->data = ch;
start = current;
start->next = new charlink;
current = start->next;
size+=1;
}
else
{
current->data = ch;
nextcur = new charlink;
current->next = nextcur;
current = nextcur;
nextcur = NULL;
size+=1;
}
}
}while(!done);
return string;
}
(C) Copyright 2005.


Ever heard of bitmap fonts? Just use those to display the string...
(I believe getch() will work when using Win32, otherwise you'll have to search through msdn for a function...)

(You can use it, just don't say it's yours. You don't even have to say I made it...)

Share this post


Link to post
Share on other sites
I'm using DirectInput already though.. Maybe I'll just use the last class I made.. Or maybe with some slight modifications..

Maybe I'll even release it.. :p

Share this post


Link to post
Share on other sites
Pardon the ugliness of this code, it's for a GUI that's currently under development, and this is something thrown together just to see if it works. It works, though I need to do some research about how to better work the 'activate' functor.


#ifndef RMS_TEXTINPUT
#define RMS_TEXTINPUT

#include "rms_basic_gui.h"
#include "rms_font.h"
#include "rmsd3dfont.h"
#include "rms_timer_renderers.h"
#include <string>
#include "charin_conversion.h"
#include "rmsrect.h"
#include "voidvoid_guiaction.h"
#include "rms_gui_exclusivity.h"

//#include "console.h"
//extern console_interface *console;

using std::string;

exclusive_group textinputs;

template <typename T>
class text_cursor:
virtual public basero {
//
// A class to invisibly render/calc the rect needed to push the cursor one way or another.
//
private:
protected:
T *cursored_text;
ro_text *invisible_text;
ro_text *txt_cursor;
string::size_type *cursor;
blinky *ro_cursor;
public:
void reset();
virtual void render(){
rendertree();
}

virtual void disable(){
// Disable cursor
//exclusive_member::disable();
ro_cursor->disable();
ro_cursor->visibility(0);

//console->newtext("textinput: disabling.");
}
virtual void enable(){
//exclusive_member::enable();
ro_cursor->enable();
ro_cursor->visibility(1);

//console->newtext("textinput: enabling.");
}


virtual bool is_enabled(){
return(ro_cursor->is_visible());
}

text_cursor(depended_rect_generator *rg, T *ct, string::size_type *c): cursored_text(ct), cursor(c), basero(rg){
calculated_font_rect_generator<percentage_rect> *cfrg=new calculated_font_rect_generator<percentage_rect>(0,0,new percentage_rect(rg,1,1,5,5));
invisible_text=new ro_text(cfrg,"",ct->font,0x00000000);
cfrg->set(&invisible_text->font, invisible_text->txtref());
invisible_text->parent=this;
children.push_back(invisible_text);

ro_cursor=new blinky(20);
ro_cursor->parent=invisible_text;
invisible_text->children.push_back(ro_cursor);

cfrg=new calculated_font_rect_generator<percentage_rect>(0,0,new percentage_rect(cfrg->dependable(),1,1,4,6));
ro_text *rot=new ro_text(cfrg,"_",ct->font,ct->color());
txt_cursor=rot;
cfrg->set(&rot->font, rot->txtref());
rot->parent=ro_cursor;
ro_cursor->children.push_back(rot);

}
virtual ~text_cursor(){
}
};


template <typename T>
void text_cursor<T>::reset(){
//
// Reset the invisible text. Essentially reset the cursor's rendering.
//
string s;

s.assign(cursored_text->str(),0,*cursor);
if (s.length() && s[s.length()-1]==' '){
s.append(".");
}
invisible_text->str(s);
invisible_text->tblrreset();
txt_cursor->tblrreset(); // overkill?
}

template <typename T,typename F>
class textinput:
virtual public basero,
public exclusive_member{
private:
protected:
F inputfunction;
T *stored_text;
text_cursor<T> *tcursor;
string::size_type cursor;
bool insertmode; // 0 == write over 1 == insert
public:
virtual void gohome();
virtual void goend();
virtual void goleft();
virtual void goright();
virtual void bkspc();
virtual void kbdel();
virtual void cleartxt();
virtual void charin(char a);
virtual void submit(){
//inputfunction(stored_text->str());
}


const string::size_type *cpos(){return(&cursor);}

//virtual void render();

virtual void enable(){
// enable cursor, set truly visible to allow for kbinput.
visibility(1);
tcursor->enable();
}
virtual void disable(){
visibility(2);
tcursor->disable();
}

virtual bool is_enabled(){
if(visible==1){
return(1);
}else{
return(0);
}
}
virtual void activate(){
group->enable(this);
}
virtual void deactivate(){
group->disable(this);
}


textinput(depended_rect_generator *rg, F inf, T *it): basero(rg), stored_text(it), cursor(0), insertmode(1), exclusive_member(&textinputs){
tcursor=new text_cursor<T>(rg,stored_text,&cursor);
tcursor->parent=stored_text;
stored_text->children.push_back(tcursor);

stored_text->parent=this;
children.push_back(stored_text);
kb.text_mode(1);
kb.textbind(charin_functor(this));
kb.keybind(KB_DEL,0,voidvoidfunctor(this,&textinput<T,F>::kbdel));
kb.keybind(KB_HOME,0,voidvoidfunctor(this,&textinput<T,F>::gohome));
kb.keybind(KB_END,0,voidvoidfunctor(this,&textinput<T,F>::goend));
kb.keybind(KB_LEFT,0,voidvoidfunctor(this,&textinput<T,F>::goleft));
kb.keybind(KB_RIGHT,0,voidvoidfunctor(this,&textinput<T,F>::goright));
kb.keybind(KB_BKSPACE,0,voidvoidfunctor(this,&textinput<T,F>::bkspc));
kb.keybind(KB_ESC,0,voidvoidfunctor(this,&textinput<T,F>::deactivate));
kb.keybind(KB_ENTER,0,voidvoidfunctor(this,&textinput<T,F>::submit));
mt.set_onclick(voidvoidfunctor(this,&textinput<T,F>::activate));

textinputs.add(this);
}
virtual ~textinput(){ textinputs.remove(this);}
};


template <typename T,typename F>
void textinput<T,F>::gohome(){ cursor=0; tcursor->reset(); }
template <typename T,typename F>
void textinput<T,F>::goend(){ cursor=stored_text->str().length(); tcursor->reset(); }
template <typename T,typename F>
void textinput<T,F>::goleft(){ if (cursor!=0){ cursor=cursor-1; tcursor->reset(); }}
template <typename T,typename F>
void textinput<T,F>::goright(){ if (cursor!=stored_text->str().length()){ cursor=cursor+1; tcursor->reset(); }}
template <typename T,typename F>
void textinput<T,F>::bkspc(){

if (cursor!=0){
string s=stored_text->str();
cursor--;
s.erase(cursor,1);
stored_text->str(s);
tcursor->reset();
}
}

template <typename T,typename F>
void textinput<T,F>::kbdel(){

if (cursor!=stored_text->str().length()){
string s=stored_text->str();
s.erase(cursor,1);
stored_text->str(s);
tcursor->reset();
}
}

template <typename T,typename F>
void textinput<T,F>::cleartxt(){

stored_text->str("");
cursor=0;
tcursor->reset();
}

template <typename T,typename F>
void textinput<T,F>::charin(char a){

string s=stored_text->str();

if (insertmode){
s.insert(cursor,&a,1);
}else{
s.replace(cursor,1,&a,1);
}
stored_text->str(s);
cursor++;
tcursor->reset();
}

#endif



I personally use windows input, but this class simply takes a char for input, no matter how you get it. Each renderable has a flag for if it takes text input [the text_mode call]. If it's set to true, it changes how the input handler determines what class gets the input. If this class is the 'top' class to get the input, the flag also tells the input handler to not pass the input to whatever functor is bound to the key in question, but to the text functor instead.

The 'exclusive_member' base class is something to ensure that only one text input is active at any given time. Having two blinking cursors confuses users!

Share this post


Link to post
Share on other sites
I did something simular in DX for a console (you know those things they have in fps games) and i found that getting the input from windows messages for text only worked the best, because windows would tell me whether it was uppercase, lower case, when the key was being repeated, and this was all pretty much free from the windows message. I believe it was WM_CHAR was what you need to catch in you MessageProc() function. Also would suggesting using a std::string

~guyaton

Share this post


Link to post
Share on other sites
The rest of the code I use for text input is a bit bugged up right now, but this function ought to help:

char nxt::DirectInput::ScanToChar(DWORD scanCode) const
{
//obtain keyboard information
static HKL layout = GetKeyboardLayout(0);
static UCHAR keyboardState[256];
if (GetKeyboardState(keyboardState) == false)
return 0;

//translate keyboard press scan code identifier to a char
UINT vk = MapVirtualKeyEx(scanCode, 1, layout);
USHORT asciiValue;
ToAscii(vk, scanCode, keyboardState, &asciiValue, 0);

return static_cast<char>(asciiValue);
}



It converts the DIK_WHATEVER value to the corresponding char. Of course, some keys DIK_F1 for example don't have a corresponding char so you'll have to filter out the results somehow.

EDIT: The functions used there are found in <windows.h>

Share this post


Link to post
Share on other sites
guyaton is correct! handling WM_CHAR is the best method for text input. I have tried to use directinput before,but it is very hard to get the resonable input speed,sometime the text input too fast, sometime too slow. with WM_CHAR, windows take care everything for you.
Blizzard use WM_CHAR as their input source in starcraft and warcraft 3, so i think there is no problem using that.

Share this post


Link to post
Share on other sites
While it seems like windows messages are the best way to go, I'm not really sure I can use it.. I suspect I'd have to do something damn ugly in order to get that working.. So I'll try load_bitmap_file's function first, and if I can't get good results, I'll try do get the WM_CHAR messages or whatever..

Thanks everyone!

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!