mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2024-12-12 13:12:14 +00:00
579502ab74
SVN r2768 (trunk)
366 lines
No EOL
11 KiB
C++
366 lines
No EOL
11 KiB
C++
/*
|
|
** menuinput.cpp
|
|
** The string input code
|
|
**
|
|
**---------------------------------------------------------------------------
|
|
** Copyright 2001-2010 Randy Heit
|
|
** Copyright 2010 Christoph Oelckers
|
|
** All rights reserved.
|
|
**
|
|
** Redistribution and use in source and binary forms, with or without
|
|
** modification, are permitted provided that the following conditions
|
|
** are met:
|
|
**
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
** notice, this list of conditions and the following disclaimer.
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
** documentation and/or other materials provided with the distribution.
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
** derived from this software without specific prior written permission.
|
|
**
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
**---------------------------------------------------------------------------
|
|
**
|
|
*/
|
|
|
|
#include "menu/menu.h"
|
|
#include "v_video.h"
|
|
#include "c_cvars.h"
|
|
#include "d_event.h"
|
|
#include "d_gui.h"
|
|
#include "v_font.h"
|
|
#include "v_palette.h"
|
|
|
|
IMPLEMENT_ABSTRACT_CLASS(DTextEnterMenu)
|
|
|
|
#define INPUTGRID_WIDTH 13
|
|
#define INPUTGRID_HEIGHT 5
|
|
|
|
// 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";
|
|
|
|
|
|
CVAR(Bool, m_showinputgrid, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|
|
|
//=============================================================================
|
|
//
|
|
//
|
|
//
|
|
//=============================================================================
|
|
|
|
DTextEnterMenu::DTextEnterMenu(DMenu *parent, char *textbuffer, int maxlen, int sizemode, bool showgrid)
|
|
: DMenu(parent)
|
|
{
|
|
mEnterString = textbuffer;
|
|
mEnterSize = maxlen;
|
|
mEnterPos = (unsigned)strlen(textbuffer);
|
|
mSizeMode = sizemode;
|
|
mInputGridOkay = showgrid || m_showinputgrid;
|
|
if (mEnterPos > 0)
|
|
{
|
|
InputGridX = INPUTGRID_WIDTH - 1;
|
|
InputGridY = INPUTGRID_HEIGHT - 1;
|
|
}
|
|
else
|
|
{
|
|
// If we are naming a new save, don't start the cursor on "end".
|
|
InputGridX = 0;
|
|
InputGridY = 0;
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
//
|
|
//
|
|
//=============================================================================
|
|
|
|
bool DTextEnterMenu::TranslateKeyboardEvents()
|
|
{
|
|
return mInputGridOkay;
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
//
|
|
//
|
|
//=============================================================================
|
|
|
|
bool DTextEnterMenu::Responder(event_t *ev)
|
|
{
|
|
if (ev->type == EV_GUI_Event)
|
|
{
|
|
// Save game and player name string input
|
|
if (ev->subtype == EV_GUI_Char)
|
|
{
|
|
mInputGridOkay = false;
|
|
if (mEnterPos < mEnterSize &&
|
|
(mSizeMode == 2/*entering player name*/ || (size_t)SmallFont->StringWidth(mEnterString) < (mEnterSize-1)*8))
|
|
{
|
|
mEnterString[mEnterPos] = (char)ev->data1;
|
|
mEnterString[++mEnterPos] = 0;
|
|
}
|
|
return true;
|
|
}
|
|
char ch = (char)ev->data1;
|
|
if ((ev->subtype == EV_GUI_KeyDown || ev->subtype == EV_GUI_KeyRepeat) && ch == '\b')
|
|
{
|
|
if (mEnterPos > 0)
|
|
{
|
|
mEnterPos--;
|
|
mEnterString[mEnterPos] = 0;
|
|
}
|
|
}
|
|
else if (ev->subtype == EV_GUI_KeyDown)
|
|
{
|
|
if (ch == GK_ESCAPE)
|
|
{
|
|
DMenu *parent = mParentMenu;
|
|
Close();
|
|
parent->MenuEvent(MKEY_Abort, false);
|
|
return true;
|
|
}
|
|
else if (ch == '\r')
|
|
{
|
|
if (mEnterString[0])
|
|
{
|
|
DMenu *parent = mParentMenu;
|
|
Close();
|
|
parent->MenuEvent(MKEY_Input, false);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
if (ev->subtype == EV_GUI_KeyDown || ev->subtype == EV_GUI_KeyRepeat)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return Super::Responder(ev);
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
//
|
|
//
|
|
//=============================================================================
|
|
|
|
bool DTextEnterMenu::MouseEvent(int type, int x, int y)
|
|
{
|
|
const int cell_width = 18 * CleanXfac;
|
|
const int cell_height = 12 * CleanYfac;
|
|
const int screen_y = screen->GetHeight() - INPUTGRID_HEIGHT * cell_height;
|
|
const int screen_x = (screen->GetWidth() - INPUTGRID_WIDTH * cell_width) / 2;
|
|
|
|
if (x >= screen_x && x < screen_x + INPUTGRID_WIDTH * cell_width && y >= screen_y)
|
|
{
|
|
InputGridX = (x - screen_x) / cell_width;
|
|
InputGridY = (y - screen_y) / cell_height;
|
|
if (type == DMenu::MOUSE_Release)
|
|
{
|
|
if (MenuEvent(MKEY_Enter, true))
|
|
{
|
|
S_Sound (CHAN_VOICE | CHAN_UI, "menu/choose", snd_menuvolume, ATTN_NONE);
|
|
if (m_use_mouse == 2) InputGridX = InputGridY = -1;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
InputGridX = InputGridY = -1;
|
|
}
|
|
return Super::MouseEvent(type, x, y);
|
|
}
|
|
|
|
|
|
|
|
//=============================================================================
|
|
//
|
|
//
|
|
//
|
|
//=============================================================================
|
|
|
|
bool DTextEnterMenu::MenuEvent (int key, bool fromcontroller)
|
|
{
|
|
if (key == MKEY_Back)
|
|
{
|
|
mParentMenu->MenuEvent(MKEY_Abort, false);
|
|
return Super::MenuEvent(key, fromcontroller);
|
|
}
|
|
if (fromcontroller)
|
|
{
|
|
mInputGridOkay = true;
|
|
}
|
|
|
|
if (mInputGridOkay)
|
|
{
|
|
int ch;
|
|
|
|
if (InputGridX == -1 || InputGridY == -1)
|
|
{
|
|
InputGridX = InputGridY = 0;
|
|
}
|
|
switch (key)
|
|
{
|
|
case MKEY_Down:
|
|
InputGridY = (InputGridY + 1) % INPUTGRID_HEIGHT;
|
|
return true;
|
|
|
|
case MKEY_Up:
|
|
InputGridY = (InputGridY + INPUTGRID_HEIGHT - 1) % INPUTGRID_HEIGHT;
|
|
return true;
|
|
|
|
case MKEY_Right:
|
|
InputGridX = (InputGridX + 1) % INPUTGRID_WIDTH;
|
|
return true;
|
|
|
|
case MKEY_Left:
|
|
InputGridX = (InputGridX + INPUTGRID_WIDTH - 1) % INPUTGRID_WIDTH;
|
|
return true;
|
|
|
|
case MKEY_Clear:
|
|
if (mEnterPos > 0)
|
|
{
|
|
mEnterString[--mEnterPos] = 0;
|
|
}
|
|
return true;
|
|
|
|
case MKEY_Enter:
|
|
assert(unsigned(InputGridX) < INPUTGRID_WIDTH && unsigned(InputGridY) < INPUTGRID_HEIGHT);
|
|
if (mInputGridOkay)
|
|
{
|
|
ch = InputGridChars[InputGridX + InputGridY * INPUTGRID_WIDTH];
|
|
if (ch == 0) // end
|
|
{
|
|
if (mEnterString[0] != '\0')
|
|
{
|
|
DMenu *parent = mParentMenu;
|
|
Close();
|
|
parent->MenuEvent(MKEY_Input, false);
|
|
return true;
|
|
}
|
|
}
|
|
else if (ch == '\b') // bs
|
|
{
|
|
if (mEnterPos > 0)
|
|
{
|
|
mEnterString[--mEnterPos] = 0;
|
|
}
|
|
}
|
|
else if (mEnterPos < mEnterSize &&
|
|
(mSizeMode == 2/*entering player name*/ || (size_t)SmallFont->StringWidth(mEnterString) < (mEnterSize-1)*8))
|
|
{
|
|
mEnterString[mEnterPos] = ch;
|
|
mEnterString[++mEnterPos] = 0;
|
|
}
|
|
}
|
|
return true;
|
|
|
|
default:
|
|
break; // Keep GCC quiet
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
//
|
|
//
|
|
//=============================================================================
|
|
|
|
void DTextEnterMenu::Drawer ()
|
|
{
|
|
mParentMenu->Drawer();
|
|
if (mInputGridOkay)
|
|
{
|
|
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() - INPUTGRID_HEIGHT * cell_height,
|
|
screen->GetWidth() /*13 * cell_width*/,
|
|
INPUTGRID_HEIGHT * cell_height);
|
|
|
|
if (InputGridX >= 0 && InputGridY >= 0)
|
|
{
|
|
// 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Super::Drawer();
|
|
} |