mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-12-15 06:51:36 +00:00
66d28a24b8
Needless to say, this is simply too volatile and would require constant active maintenance, not to mention a huge amount of work up front to get going. It also hid a nasty problem with the Destroy method. Due to the way the garbage collector works, Destroy cannot be exposed to scripts as-is. It may be called from scripts but it may not be overridden from scripts because the garbage collector can call this function after all data needed for calling a scripted override has already been destroyed because if that data is also being collected there is no guarantee that proper order of destruction is observed. So for now Destroy is just a normal native method to scripted classes
375 lines
No EOL
11 KiB
C++
375 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"
|
|
#include "cmdlib.h"
|
|
// [TP] New #includes
|
|
#include "v_text.h"
|
|
|
|
IMPLEMENT_CLASS(DTextEnterMenu, true, false)
|
|
|
|
#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)
|
|
|
|
//=============================================================================
|
|
//
|
|
//
|
|
//
|
|
//=============================================================================
|
|
|
|
// [TP] Added allowcolors
|
|
DTextEnterMenu::DTextEnterMenu(DMenu *parent, char *textbuffer, int maxlen, int sizemode, bool showgrid, bool allowcolors)
|
|
: 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;
|
|
}
|
|
AllowColors = allowcolors; // [TP]
|
|
}
|
|
|
|
//=============================================================================
|
|
//
|
|
//
|
|
//
|
|
//=============================================================================
|
|
|
|
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])
|
|
{
|
|
// [TP] If we allow color codes, colorize the string now.
|
|
if (AllowColors)
|
|
strbin(mEnterString);
|
|
|
|
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();
|
|
} |