mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2024-11-16 09:11:17 +00:00
- Added an input grid for controller-based string entry in the save game and player setup menus
(which I had forgotten to do for 2.4.0). If you don't use a controller to activate the string entry mode and keep your hands off the controller while typing, it remains invisible. SVN r2121 (trunk)
This commit is contained in:
parent
71b75f0d7a
commit
7511c5385b
1 changed files with 182 additions and 11 deletions
193
src/m_menu.cpp
193
src/m_menu.cpp
|
@ -82,6 +82,9 @@
|
||||||
#define KEY_REPEAT_DELAY (TICRATE*5/12)
|
#define KEY_REPEAT_DELAY (TICRATE*5/12)
|
||||||
#define KEY_REPEAT_RATE (3)
|
#define KEY_REPEAT_RATE (3)
|
||||||
|
|
||||||
|
#define INPUTGRID_WIDTH 13
|
||||||
|
#define INPUTGRID_HEIGHT 5
|
||||||
|
|
||||||
// TYPES -------------------------------------------------------------------
|
// TYPES -------------------------------------------------------------------
|
||||||
|
|
||||||
struct FSaveGameNode : public Node
|
struct FSaveGameNode : public Node
|
||||||
|
@ -171,6 +174,8 @@ static void M_DrawFiles ();
|
||||||
void M_DrawFrame (int x, int y, int width, int height);
|
void M_DrawFrame (int x, int y, int width, int height);
|
||||||
static void M_DrawSaveLoadBorder (int x,int y, int len);
|
static void M_DrawSaveLoadBorder (int x,int y, int len);
|
||||||
static void M_DrawSaveLoadCommon ();
|
static void M_DrawSaveLoadCommon ();
|
||||||
|
static void M_DrawInputGrid();
|
||||||
|
|
||||||
static void M_SetupNextMenu (oldmenu_t *menudef);
|
static void M_SetupNextMenu (oldmenu_t *menudef);
|
||||||
static void M_StartMessage (const char *string, void(*routine)(int));
|
static void M_StartMessage (const char *string, void(*routine)(int));
|
||||||
static void M_EndMessage (int key);
|
static void M_EndMessage (int key);
|
||||||
|
@ -276,6 +281,18 @@ static int epi; // Selected episode
|
||||||
|
|
||||||
static const char *saved_playerclass = NULL;
|
static const char *saved_playerclass = NULL;
|
||||||
|
|
||||||
|
// Heretic and Hexen do not, by default, come with glyphs for all of these
|
||||||
|
// characters. Oh well. Doom and Strife do.
|
||||||
|
static const char InputGridChars[INPUTGRID_WIDTH * INPUTGRID_HEIGHT] =
|
||||||
|
"ABCDEFGHIJKLM"
|
||||||
|
"NOPQRSTUVWXYZ"
|
||||||
|
"0123456789+-="
|
||||||
|
".,!?@'\":;[]()"
|
||||||
|
"<>^#$%&*/_ \b";
|
||||||
|
static int InputGridX = INPUTGRID_WIDTH - 1;
|
||||||
|
static int InputGridY = INPUTGRID_HEIGHT - 1;
|
||||||
|
static bool InputGridOkay; // Last input was with a controller.
|
||||||
|
|
||||||
// PRIVATE MENU DEFINITIONS ------------------------------------------------
|
// PRIVATE MENU DEFINITIONS ------------------------------------------------
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -2873,14 +2890,6 @@ bool M_Responder (event_t *ev)
|
||||||
// This code previously listened for EV_GUI_KeyRepeat to handle repeating
|
// This code previously listened for EV_GUI_KeyRepeat to handle repeating
|
||||||
// in the menus, but that doesn't work with gamepads, so now we combine
|
// in the menus, but that doesn't work with gamepads, so now we combine
|
||||||
// the multiple inputs into buttons and handle the repetition manually.
|
// the multiple inputs into buttons and handle the repetition manually.
|
||||||
//
|
|
||||||
// FIXME: genStringEnter and messageToPrint do not play well with game
|
|
||||||
// controllers. In fact, you can still interact with the rest of
|
|
||||||
// the menu using a controller while the menu waits for input. Especially
|
|
||||||
// bad if you, say, select quit from the main menu. (Ideally,
|
|
||||||
// genStringEnter will pop up a keypad if it detects a controller is
|
|
||||||
// being used to navigate the menu. At the very least, it should
|
|
||||||
// disable the controller.)
|
|
||||||
if (ev->type == EV_GUI_Event)
|
if (ev->type == EV_GUI_Event)
|
||||||
{
|
{
|
||||||
// Save game and player name string input
|
// Save game and player name string input
|
||||||
|
@ -2888,8 +2897,9 @@ bool M_Responder (event_t *ev)
|
||||||
{
|
{
|
||||||
if (ev->subtype == EV_GUI_Char)
|
if (ev->subtype == EV_GUI_Char)
|
||||||
{
|
{
|
||||||
|
InputGridOkay = false;
|
||||||
if (saveCharIndex < genStringLen &&
|
if (saveCharIndex < genStringLen &&
|
||||||
(genStringEnter == 2 || (size_t)SmallFont->StringWidth(savegamestring) < (genStringLen-1)*8))
|
(genStringEnter == 2/*entering player name*/ || (size_t)SmallFont->StringWidth(savegamestring) < (genStringLen-1)*8))
|
||||||
{
|
{
|
||||||
savegamestring[saveCharIndex] = (char)ev->data1;
|
savegamestring[saveCharIndex] = (char)ev->data1;
|
||||||
savegamestring[++saveCharIndex] = 0;
|
savegamestring[++saveCharIndex] = 0;
|
||||||
|
@ -2897,8 +2907,7 @@ bool M_Responder (event_t *ev)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
ch = ev->data1;
|
ch = ev->data1;
|
||||||
if ((ev->subtype == EV_GUI_KeyDown || ev->subtype == EV_GUI_KeyRepeat) &&
|
if ((ev->subtype == EV_GUI_KeyDown || ev->subtype == EV_GUI_KeyRepeat) && ch == '\b')
|
||||||
ch == '\b')
|
|
||||||
{
|
{
|
||||||
if (saveCharIndex > 0)
|
if (saveCharIndex > 0)
|
||||||
{
|
{
|
||||||
|
@ -3011,10 +3020,23 @@ bool M_Responder (event_t *ev)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (!keyup)
|
||||||
|
{
|
||||||
|
InputGridOkay = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (ev->type == EV_KeyDown || ev->type == EV_KeyUp)
|
else if (ev->type == EV_KeyDown || ev->type == EV_KeyUp)
|
||||||
{
|
{
|
||||||
keyup = ev->type == EV_KeyUp;
|
keyup = ev->type == EV_KeyUp;
|
||||||
|
// If this is a button down, it's okay to show the input grid if the
|
||||||
|
// next action causes us to enter genStringEnter mode. If we are
|
||||||
|
// already in that mode, then we let M_ButtonHandler() turn it on so
|
||||||
|
// that it will know if a button press happened while the input grid
|
||||||
|
// was turned off.
|
||||||
|
if (!keyup && !genStringEnter)
|
||||||
|
{
|
||||||
|
InputGridOkay = true;
|
||||||
|
}
|
||||||
ch = ev->data1;
|
ch = ev->data1;
|
||||||
switch (ch)
|
switch (ch)
|
||||||
{
|
{
|
||||||
|
@ -3145,6 +3167,74 @@ void M_ButtonHandler(EMenuKey key, bool repeat)
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (genStringEnter)
|
||||||
|
{
|
||||||
|
int ch;
|
||||||
|
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
case MKEY_Down:
|
||||||
|
InputGridY = (InputGridY + 1) % INPUTGRID_HEIGHT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MKEY_Up:
|
||||||
|
InputGridY = (InputGridY + INPUTGRID_HEIGHT - 1) % INPUTGRID_HEIGHT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MKEY_Right:
|
||||||
|
InputGridX = (InputGridX + 1) % INPUTGRID_WIDTH;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MKEY_Left:
|
||||||
|
InputGridX = (InputGridX + INPUTGRID_WIDTH - 1) % INPUTGRID_WIDTH;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MKEY_Clear:
|
||||||
|
if (saveCharIndex > 0)
|
||||||
|
{
|
||||||
|
savegamestring[--saveCharIndex] = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MKEY_Enter:
|
||||||
|
assert(unsigned(InputGridX) < INPUTGRID_WIDTH && unsigned(InputGridY) < INPUTGRID_HEIGHT);
|
||||||
|
if (InputGridOkay)
|
||||||
|
{
|
||||||
|
ch = InputGridChars[InputGridX + InputGridY * INPUTGRID_WIDTH];
|
||||||
|
if (ch == 0) // end
|
||||||
|
{
|
||||||
|
if (savegamestring[0] != '\0')
|
||||||
|
{
|
||||||
|
genStringEnter = 0;
|
||||||
|
if (messageToPrint)
|
||||||
|
{
|
||||||
|
M_ClearMenus();
|
||||||
|
}
|
||||||
|
genStringEnd(SelSaveGame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ch == '\b') // bs
|
||||||
|
{
|
||||||
|
if (saveCharIndex > 0)
|
||||||
|
{
|
||||||
|
savegamestring[--saveCharIndex] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (saveCharIndex < genStringLen &&
|
||||||
|
(genStringEnter == 2/*entering player name*/ || (size_t)SmallFont->StringWidth(savegamestring) < (genStringLen-1)*8))
|
||||||
|
{
|
||||||
|
savegamestring[saveCharIndex] = ch;
|
||||||
|
savegamestring[++saveCharIndex] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break; // Keep GCC quiet
|
||||||
|
}
|
||||||
|
InputGridOkay = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (currentMenu == &SaveDef || currentMenu == &LoadDef)
|
if (currentMenu == &SaveDef || currentMenu == &LoadDef)
|
||||||
{
|
{
|
||||||
M_SaveLoadButtonHandler(key);
|
M_SaveLoadButtonHandler(key);
|
||||||
|
@ -3359,6 +3449,12 @@ static void M_SaveSelect (const FSaveGameNode *file)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// If we are naming a new save, don't start the cursor on "end".
|
||||||
|
if (InputGridX == INPUTGRID_WIDTH - 1 && InputGridY == INPUTGRID_HEIGHT - 1)
|
||||||
|
{
|
||||||
|
InputGridX = 0;
|
||||||
|
InputGridY = 0;
|
||||||
|
}
|
||||||
savegamestring[0] = 0;
|
savegamestring[0] = 0;
|
||||||
}
|
}
|
||||||
saveCharIndex = strlen (savegamestring);
|
saveCharIndex = strlen (savegamestring);
|
||||||
|
@ -3577,6 +3673,10 @@ void M_Drawer ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (genStringEnter && InputGridOkay)
|
||||||
|
{
|
||||||
|
M_DrawInputGrid();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3602,6 +3702,77 @@ static void M_ClearSaveStuff ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void M_DrawInputGrid()
|
||||||
|
{
|
||||||
|
const int cell_width = 18 * CleanXfac;
|
||||||
|
const int cell_height = 12 * CleanYfac;
|
||||||
|
const int top_padding = cell_height / 2 - SmallFont->GetHeight() * CleanYfac / 2;
|
||||||
|
|
||||||
|
// Darken the background behind the character grid.
|
||||||
|
// Unless we frame it with a border, I think it looks better to extend the
|
||||||
|
// background across the full width of the screen.
|
||||||
|
screen->Dim(0, 0.8f,
|
||||||
|
0 /*screen->GetWidth()/2 - 13 * cell_width / 2*/,
|
||||||
|
screen->GetHeight() - 5 * cell_height,
|
||||||
|
screen->GetWidth() /*13 * cell_width*/,
|
||||||
|
5 * cell_height);
|
||||||
|
|
||||||
|
// Highlight the background behind the selected character.
|
||||||
|
screen->Dim(MAKERGB(255,248,220), 0.6f,
|
||||||
|
InputGridX * cell_width - INPUTGRID_WIDTH * cell_width / 2 + screen->GetWidth() / 2,
|
||||||
|
InputGridY * cell_height - INPUTGRID_HEIGHT * cell_height + screen->GetHeight(),
|
||||||
|
cell_width, cell_height);
|
||||||
|
|
||||||
|
for (int y = 0; y < INPUTGRID_HEIGHT; ++y)
|
||||||
|
{
|
||||||
|
const int yy = y * cell_height - INPUTGRID_HEIGHT * cell_height + screen->GetHeight();
|
||||||
|
for (int x = 0; x < INPUTGRID_WIDTH; ++x)
|
||||||
|
{
|
||||||
|
int width;
|
||||||
|
const int xx = x * cell_width - INPUTGRID_WIDTH * cell_width / 2 + screen->GetWidth() / 2;
|
||||||
|
const int ch = InputGridChars[y * INPUTGRID_WIDTH + x];
|
||||||
|
FTexture *pic = SmallFont->GetChar(ch, &width);
|
||||||
|
EColorRange color;
|
||||||
|
FRemapTable *remap;
|
||||||
|
|
||||||
|
// The highlighted character is yellow; the rest are dark gray.
|
||||||
|
color = (x == InputGridX && y == InputGridY) ? CR_YELLOW : CR_DARKGRAY;
|
||||||
|
remap = SmallFont->GetColorTranslation(color);
|
||||||
|
|
||||||
|
if (pic != NULL)
|
||||||
|
{
|
||||||
|
// Draw a normal character.
|
||||||
|
screen->DrawTexture(pic, xx + cell_width/2 - width*CleanXfac/2, yy + top_padding,
|
||||||
|
DTA_Translation, remap,
|
||||||
|
DTA_CleanNoMove, true,
|
||||||
|
TAG_DONE);
|
||||||
|
}
|
||||||
|
else if (ch == ' ')
|
||||||
|
{
|
||||||
|
// Draw the space as a box outline. We also draw it 50% wider than it really is.
|
||||||
|
const int x1 = xx + cell_width/2 - width * CleanXfac * 3 / 4;
|
||||||
|
const int x2 = x1 + width * 3 * CleanXfac / 2;
|
||||||
|
const int y1 = yy + top_padding;
|
||||||
|
const int y2 = y1 + SmallFont->GetHeight() * CleanYfac;
|
||||||
|
const int palentry = remap->Remap[remap->NumEntries*2/3];
|
||||||
|
const uint32 palcolor = remap->Palette[remap->NumEntries*2/3];
|
||||||
|
screen->Clear(x1, y1, x2, y1+CleanYfac, palentry, palcolor); // top
|
||||||
|
screen->Clear(x1, y2, x2, y2+CleanYfac, palentry, palcolor); // bottom
|
||||||
|
screen->Clear(x1, y1+CleanYfac, x1+CleanXfac, y2, palentry, palcolor); // left
|
||||||
|
screen->Clear(x2-CleanXfac, y1+CleanYfac, x2, y2, palentry, palcolor); // right
|
||||||
|
}
|
||||||
|
else if (ch == '\b' || ch == 0)
|
||||||
|
{
|
||||||
|
// Draw the backspace and end "characters".
|
||||||
|
const char *const str = ch == '\b' ? "BS" : "ED";
|
||||||
|
screen->DrawText(SmallFont, color,
|
||||||
|
xx + cell_width/2 - SmallFont->StringWidth(str)*CleanXfac/2,
|
||||||
|
yy + top_padding, str, DTA_CleanNoMove, true, TAG_DONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// M_ClearMenus
|
// M_ClearMenus
|
||||||
//
|
//
|
||||||
|
|
Loading…
Reference in a new issue