Merge pull request 'Unify Text Input' (#78) from unified-text-input into blankart-dev
Reviewed-on: https://codeberg.org/NepDisk/blankart/pulls/78
This commit is contained in:
commit
8463d6abcc
13 changed files with 724 additions and 653 deletions
|
|
@ -441,4 +441,3 @@ if("${CMAKE_SYSTEM_NAME}" STREQUAL Windows AND NOT "${SRB2_CONFIG_INTERNAL_LIBRA
|
|||
COMMENT "Copying runtime DLLs"
|
||||
)
|
||||
endif()
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ m_cond.c
|
|||
m_easing.c
|
||||
m_fixed.c
|
||||
m_menu.c
|
||||
m_textinput.c
|
||||
m_memcpy.c
|
||||
m_misc.cpp
|
||||
m_perfstats.c
|
||||
|
|
|
|||
342
src/console.c
342
src/console.c
|
|
@ -32,6 +32,7 @@
|
|||
#include "i_threads.h"
|
||||
#include "d_main.h"
|
||||
#include "m_menu.h"
|
||||
#include "m_textinput.h"
|
||||
#include "filesrch.h"
|
||||
#include "m_misc.h"
|
||||
|
||||
|
|
@ -101,9 +102,7 @@ static char inputlines[32][CON_MAXPROMPTCHARS]; // hold last 32 prompt lines
|
|||
|
||||
static INT32 inputline; // current input line number
|
||||
static INT32 inputhist; // line number of history input line to restore
|
||||
static size_t input_cur; // position of cursor in line
|
||||
static size_t input_sel; // position of selection marker (I.E.: anything between this and input_cur is "selected")
|
||||
static size_t input_len; // length of current line, used to bound cursor and such
|
||||
static textinput_t input;
|
||||
// notice: input does NOT include the "$" at the start of the line. - 11/3/16
|
||||
|
||||
// protos.
|
||||
|
|
@ -502,7 +501,8 @@ static void CON_InputInit(void)
|
|||
// prepare the first prompt line
|
||||
memset(inputlines, 0, sizeof (inputlines));
|
||||
inputline = 0;
|
||||
input_cur = input_sel = input_len = 0;
|
||||
|
||||
M_TextInputInit(&input, inputlines[inputline], CON_MAXPROMPTCHARS);
|
||||
|
||||
Unlock_state();
|
||||
}
|
||||
|
|
@ -799,119 +799,6 @@ void CON_Ticker(void)
|
|||
Unlock_state();
|
||||
}
|
||||
|
||||
//
|
||||
// ----
|
||||
//
|
||||
// Shortcuts for adding and deleting characters, strings, and sections
|
||||
// Necessary due to moving cursor
|
||||
//
|
||||
|
||||
static void CON_InputClear(void)
|
||||
{
|
||||
Lock_state();
|
||||
|
||||
memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
|
||||
input_cur = input_sel = input_len = 0;
|
||||
|
||||
Unlock_state();
|
||||
}
|
||||
|
||||
static void CON_InputSetString(const char *c)
|
||||
{
|
||||
Lock_state();
|
||||
|
||||
memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
|
||||
strcpy(inputlines[inputline], c);
|
||||
input_cur = input_sel = input_len = strlen(c);
|
||||
|
||||
Unlock_state();
|
||||
}
|
||||
|
||||
static void CON_InputAddString(const char *c)
|
||||
{
|
||||
size_t csize = strlen(c);
|
||||
|
||||
Lock_state();
|
||||
|
||||
if (input_len + csize > CON_MAXPROMPTCHARS-1)
|
||||
{
|
||||
Unlock_state();
|
||||
return;
|
||||
}
|
||||
if (input_cur != input_len)
|
||||
memmove(&inputlines[inputline][input_cur+csize], &inputlines[inputline][input_cur], input_len-input_cur);
|
||||
memcpy(&inputlines[inputline][input_cur], c, csize);
|
||||
input_len += csize;
|
||||
input_sel = (input_cur += csize);
|
||||
|
||||
Unlock_state();
|
||||
}
|
||||
|
||||
static void CON_InputDelSelection(void)
|
||||
{
|
||||
size_t start, end, len;
|
||||
|
||||
Lock_state();
|
||||
|
||||
if (!input_cur)
|
||||
{
|
||||
Unlock_state();
|
||||
return;
|
||||
}
|
||||
|
||||
if (input_cur > input_sel)
|
||||
{
|
||||
start = input_sel;
|
||||
end = input_cur;
|
||||
}
|
||||
else
|
||||
{
|
||||
start = input_cur;
|
||||
end = input_sel;
|
||||
}
|
||||
len = (end - start);
|
||||
|
||||
if (end != input_len)
|
||||
memmove(&inputlines[inputline][start], &inputlines[inputline][end], input_len-end);
|
||||
memset(&inputlines[inputline][input_len - len], 0, len);
|
||||
|
||||
input_len -= len;
|
||||
input_sel = input_cur = start;
|
||||
|
||||
Unlock_state();
|
||||
}
|
||||
|
||||
static void CON_InputAddChar(char c)
|
||||
{
|
||||
if (input_len >= CON_MAXPROMPTCHARS-1)
|
||||
return;
|
||||
|
||||
Lock_state();
|
||||
|
||||
if (input_cur != input_len)
|
||||
memmove(&inputlines[inputline][input_cur+1], &inputlines[inputline][input_cur], input_len-input_cur);
|
||||
inputlines[inputline][input_cur++] = c;
|
||||
inputlines[inputline][++input_len] = 0;
|
||||
input_sel = input_cur;
|
||||
|
||||
Unlock_state();
|
||||
}
|
||||
|
||||
static void CON_InputDelChar(void)
|
||||
{
|
||||
if (!input_cur)
|
||||
return;
|
||||
|
||||
Lock_state();
|
||||
|
||||
if (input_cur != input_len)
|
||||
memmove(&inputlines[inputline][input_cur-1], &inputlines[inputline][input_cur], input_len-input_cur);
|
||||
inputlines[inputline][--input_len] = 0;
|
||||
input_sel = --input_cur;
|
||||
|
||||
Unlock_state();
|
||||
}
|
||||
|
||||
//
|
||||
// ----
|
||||
//
|
||||
|
|
@ -995,77 +882,16 @@ boolean CON_Responder(event_t *ev)
|
|||
}
|
||||
}
|
||||
|
||||
Lock_state();
|
||||
M_TextInputHandle(&input, key);
|
||||
Unlock_state();
|
||||
|
||||
// Always eat ctrl/shift/alt if console open, so the menu doesn't get ideas
|
||||
if (key == KEY_LSHIFT || key == KEY_RSHIFT
|
||||
|| key == KEY_LCTRL || key == KEY_RCTRL
|
||||
|| key == KEY_LALT || key == KEY_RALT)
|
||||
return true;
|
||||
|
||||
if (key == KEY_LEFTARROW)
|
||||
{
|
||||
if (input_cur != 0)
|
||||
{
|
||||
if (ctrldown)
|
||||
input_cur = M_JumpWordReverse(inputlines[inputline], input_cur);
|
||||
else
|
||||
--input_cur;
|
||||
}
|
||||
if (!shiftdown)
|
||||
input_sel = input_cur;
|
||||
return true;
|
||||
}
|
||||
else if (key == KEY_RIGHTARROW)
|
||||
{
|
||||
if (input_cur < input_len)
|
||||
{
|
||||
if (ctrldown)
|
||||
input_cur += M_JumpWord(&inputlines[inputline][input_cur]);
|
||||
else
|
||||
++input_cur;
|
||||
}
|
||||
if (!shiftdown)
|
||||
input_sel = input_cur;
|
||||
return true;
|
||||
}
|
||||
|
||||
// backspace and delete command prompt
|
||||
if (input_sel != input_cur)
|
||||
{
|
||||
if (key == KEY_BACKSPACE || key == KEY_DEL)
|
||||
{
|
||||
CON_InputDelSelection();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (key == KEY_BACKSPACE)
|
||||
{
|
||||
if (ctrldown)
|
||||
{
|
||||
input_sel = M_JumpWordReverse(inputlines[inputline], input_cur);
|
||||
CON_InputDelSelection();
|
||||
}
|
||||
else
|
||||
CON_InputDelChar();
|
||||
return true;
|
||||
}
|
||||
else if (key == KEY_DEL)
|
||||
{
|
||||
if (input_cur == input_len)
|
||||
return true;
|
||||
|
||||
if (ctrldown)
|
||||
{
|
||||
input_sel = input_cur + M_JumpWord(&inputlines[inputline][input_cur]);
|
||||
CON_InputDelSelection();
|
||||
}
|
||||
else
|
||||
{
|
||||
++input_cur;
|
||||
CON_InputDelChar();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ctrl modifier -- changes behavior, adds shortcuts
|
||||
if (ctrldown)
|
||||
{
|
||||
|
|
@ -1076,9 +902,9 @@ boolean CON_Responder(event_t *ev)
|
|||
|
||||
if (!completion[0])
|
||||
{
|
||||
if (!input_len || input_len >= 40 || strchr(inputlines[inputline], ' '))
|
||||
if (!input.length || input.length >= 40 || strchr(input.buffer, ' '))
|
||||
return true;
|
||||
strcpy(completion, inputlines[inputline]);
|
||||
strcpy(completion, input.buffer);
|
||||
}
|
||||
len = strlen(completion);
|
||||
|
||||
|
|
@ -1117,42 +943,9 @@ boolean CON_Responder(event_t *ev)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (key == 'x' || key == 'X')
|
||||
{
|
||||
if (input_sel > input_cur)
|
||||
I_ClipboardCopy(&inputlines[inputline][input_cur], input_sel-input_cur);
|
||||
else
|
||||
I_ClipboardCopy(&inputlines[inputline][input_sel], input_cur-input_sel);
|
||||
CON_InputDelSelection();
|
||||
// Those are already handled in M_TextInputHandle, but they do extra logic... Maybe textinput_t should have some sort of callbacks?
|
||||
if (key == 'x' || key == 'X' || key == 'v' || key == 'V')
|
||||
completion[0] = 0;
|
||||
return true;
|
||||
}
|
||||
else if (key == 'c' || key == 'C')
|
||||
{
|
||||
if (input_sel > input_cur)
|
||||
I_ClipboardCopy(&inputlines[inputline][input_cur], input_sel-input_cur);
|
||||
else
|
||||
I_ClipboardCopy(&inputlines[inputline][input_sel], input_cur-input_sel);
|
||||
return true;
|
||||
}
|
||||
else if (key == 'v' || key == 'V')
|
||||
{
|
||||
const char *paste = I_ClipboardPaste();
|
||||
if (input_sel != input_cur)
|
||||
CON_InputDelSelection();
|
||||
if (paste != NULL)
|
||||
CON_InputAddString(paste);
|
||||
completion[0] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Select all
|
||||
if (key == 'a' || key == 'A')
|
||||
{
|
||||
input_sel = 0;
|
||||
input_cur = input_len;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ...why shouldn't it eat the key? if it doesn't, it just means you
|
||||
// can control Sonic from the console, which is silly
|
||||
|
|
@ -1167,9 +960,9 @@ boolean CON_Responder(event_t *ev)
|
|||
// remember typing for several completions (a-la-4dos)
|
||||
if (!completion[0])
|
||||
{
|
||||
if (!input_len || input_len >= 40 || strchr(inputlines[inputline], ' '))
|
||||
if (!input.length || input.length >= 40 || strchr(input.buffer, ' '))
|
||||
return true;
|
||||
strcpy(completion, inputlines[inputline]);
|
||||
strcpy(completion, input.buffer);
|
||||
skips = 0;
|
||||
com_skips = 0;
|
||||
var_skips = 0;
|
||||
|
|
@ -1223,7 +1016,9 @@ boolean CON_Responder(event_t *ev)
|
|||
|
||||
if (cmd)
|
||||
{
|
||||
CON_InputSetString(va("%s ", cmd));
|
||||
Lock_state();
|
||||
M_TextInputSetString(&input, va("%s ", cmd));
|
||||
Unlock_state();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1246,20 +1041,6 @@ boolean CON_Responder(event_t *ev)
|
|||
con_scrollup--;
|
||||
return true;
|
||||
}
|
||||
else if (key == KEY_HOME)
|
||||
{
|
||||
input_cur = 0;
|
||||
if (!shiftdown)
|
||||
input_sel = input_cur;
|
||||
return true;
|
||||
}
|
||||
else if (key == KEY_END)
|
||||
{
|
||||
input_cur = input_len;
|
||||
if (!shiftdown)
|
||||
input_sel = input_cur;
|
||||
return true;
|
||||
}
|
||||
|
||||
// At this point we're messing with input
|
||||
// Clear completion
|
||||
|
|
@ -1268,21 +1049,28 @@ boolean CON_Responder(event_t *ev)
|
|||
// command enter
|
||||
if (key == KEY_ENTER)
|
||||
{
|
||||
if (!input_len)
|
||||
if (!input.length)
|
||||
return true;
|
||||
|
||||
// push the command
|
||||
COM_BufAddText(inputlines[inputline]);
|
||||
COM_BufAddText(input.buffer);
|
||||
COM_BufAddText("\n");
|
||||
|
||||
CONS_Printf("\x86""%c""\x80""%s\n", CON_PROMPTCHAR, inputlines[inputline]);
|
||||
|
||||
Lock_state();
|
||||
|
||||
// Only add command to history if it differs from previous one
|
||||
if (strcmp(inputlines[inputline], inputlines[(inputline-1) & 31]))
|
||||
if (strcmp(input.buffer, inputlines[(inputline-1) & 31]))
|
||||
{
|
||||
inputline = (inputline+1) & 31;
|
||||
M_TextInputInit(&input, inputlines[inputline], CON_MAXPROMPTCHARS);
|
||||
}
|
||||
|
||||
inputhist = inputline;
|
||||
CON_InputClear();
|
||||
M_TextInputClear(&input);
|
||||
|
||||
Unlock_state();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1300,7 +1088,9 @@ boolean CON_Responder(event_t *ev)
|
|||
if (inputhist == inputline)
|
||||
inputhist = (inputline + 1) & 31;
|
||||
|
||||
CON_InputSetString(inputlines[inputhist]);
|
||||
Lock_state();
|
||||
M_TextInputSetString(&input, inputlines[inputhist]);
|
||||
Unlock_state();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1314,46 +1104,16 @@ boolean CON_Responder(event_t *ev)
|
|||
while (inputhist != inputline && !inputlines[inputhist][0]);
|
||||
|
||||
// back to currentline
|
||||
Lock_state();
|
||||
if (inputhist == inputline)
|
||||
CON_InputClear();
|
||||
M_TextInputClear(&input);
|
||||
else
|
||||
CON_InputSetString(inputlines[inputhist]);
|
||||
M_TextInputSetString(&input, inputlines[inputhist]);
|
||||
Unlock_state();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// allow people to use keypad in console (good for typing IP addresses) - Calum
|
||||
if (key >= KEY_KEYPAD7 && key <= KEY_KPADDEL)
|
||||
{
|
||||
char keypad_translation[] = {'7','8','9','-',
|
||||
'4','5','6','+',
|
||||
'1','2','3',
|
||||
'0','.'};
|
||||
|
||||
key = keypad_translation[key - KEY_KEYPAD7];
|
||||
}
|
||||
else if (key == KEY_KPADSLASH)
|
||||
key = '/';
|
||||
|
||||
// same capslock code as hu_stuff.c's HU_responder. Check there for details.
|
||||
if ((key >= 'a' && key <= 'z') || (key >= 'A' && key <= 'Z'))
|
||||
{
|
||||
if (shiftdown ^ capslock)
|
||||
key = shiftxform[key];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (shiftdown)
|
||||
key = shiftxform[key];
|
||||
}
|
||||
|
||||
// enter a char into the command prompt
|
||||
if (key < 32 || key > 127)
|
||||
return true;
|
||||
|
||||
if (input_sel != input_cur)
|
||||
CON_InputDelSelection();
|
||||
CON_InputAddChar(key);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1618,16 +1378,16 @@ static void CON_DrawInput(void)
|
|||
|
||||
clen = con_width-13;
|
||||
|
||||
if (input_len <= clen)
|
||||
if (input.length <= clen)
|
||||
{
|
||||
c = 0;
|
||||
clen = input_len;
|
||||
clen = input.length;
|
||||
}
|
||||
else // input line scrolls left if it gets too long
|
||||
{
|
||||
clen -= 2; // There will always be some extra truncation -- but where is what we'll find out
|
||||
|
||||
if (input_cur <= clen/2)
|
||||
if (input.cursor <= clen/2)
|
||||
{
|
||||
// Close enough to right edge to show all
|
||||
c = 0;
|
||||
|
|
@ -1638,15 +1398,15 @@ static void CON_DrawInput(void)
|
|||
{
|
||||
// Cursor in the middle (or right side) of input
|
||||
// Move over for the ellipsis
|
||||
c = input_cur - (clen/2) + 2;
|
||||
c = input.cursor - (clen/2) + 2;
|
||||
x += charwidth*2;
|
||||
lellip = 1;
|
||||
|
||||
if (c + clen >= input_len)
|
||||
if (c + clen >= input.length)
|
||||
{
|
||||
// Cursor in the right side of input
|
||||
// We were too far over, so move back
|
||||
c = input_len - clen;
|
||||
c = input.length - clen;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1660,8 +1420,8 @@ static void CON_DrawInput(void)
|
|||
if (lellip)
|
||||
{
|
||||
x -= charwidth*3;
|
||||
if (input_sel < c)
|
||||
V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 77 | V_NOSCALESTART);
|
||||
if (input.select < c)
|
||||
V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 107 | V_NOSCALESTART);
|
||||
for (i = 0; i < 3; ++i, x += charwidth)
|
||||
V_DrawCharacter(x, y, '.' | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, true);
|
||||
}
|
||||
|
|
@ -1670,7 +1430,7 @@ static void CON_DrawInput(void)
|
|||
|
||||
for (cend = c + clen; c < cend; ++c, x += charwidth)
|
||||
{
|
||||
if ((input_sel > c && input_cur <= c) || (input_sel <= c && input_cur > c))
|
||||
if ((input.select > c && input.cursor <= c) || (input.select <= c && input.cursor > c))
|
||||
{
|
||||
V_DrawFill(x, y, charwidth, (10 * con_scalefactor), 77 | V_NOSCALESTART);
|
||||
V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_YELLOWMAP | V_NOSCALESTART, true);
|
||||
|
|
@ -1678,15 +1438,15 @@ static void CON_DrawInput(void)
|
|||
else
|
||||
V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_NOSCALESTART, true);
|
||||
|
||||
if (c == input_cur && con_tick >= 4)
|
||||
V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, true);
|
||||
if (c == input.cursor && con_tick >= 4)
|
||||
V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, !cv_menucaps.value);
|
||||
}
|
||||
if (cend == input_cur && con_tick >= 4)
|
||||
V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, true);
|
||||
if (cend == input.cursor && con_tick >= 4)
|
||||
V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, !cv_menucaps.value);
|
||||
if (rellip)
|
||||
{
|
||||
if (input_sel > cend)
|
||||
V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 77 | V_NOSCALESTART);
|
||||
if (input.select > cend)
|
||||
V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 107 | V_NOSCALESTART);
|
||||
for (i = 0; i < 3; ++i, x += charwidth)
|
||||
V_DrawCharacter(x, y, '.' | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, true);
|
||||
}
|
||||
|
|
|
|||
39
src/g_demo.c
39
src/g_demo.c
|
|
@ -28,6 +28,7 @@
|
|||
#include "m_misc.h"
|
||||
#include "m_menu.h"
|
||||
#include "m_argv.h"
|
||||
#include "m_textinput.h"
|
||||
#include "hu_stuff.h"
|
||||
#include "z_zone.h"
|
||||
#include "i_video.h"
|
||||
|
|
@ -2776,7 +2777,19 @@ void G_BeginRecording(void)
|
|||
|
||||
// Full replay title
|
||||
demobuf.p += 64;
|
||||
snprintf(demo.titlename, 64, "%s - %s", G_BuildMapTitle(gamemap), modeattacking ? "Record Attack" : connectedservername);
|
||||
{
|
||||
char demotitlename[65];
|
||||
char *title = G_BuildMapTitle(gamemap);
|
||||
// Print to a separate temp buffer instead of demo.titlename, so we can use it in M_TextInputSetString
|
||||
snprintf(demotitlename, 64, "%s - %s", title, modeattacking ? "Time Attack" : connectedservername);
|
||||
|
||||
// Init just in case it isn't initialized already
|
||||
M_TextInputInit(&demo.titlenameinput, demo.titlename, sizeof(demo.titlename));
|
||||
|
||||
// This will indirectly assign to demo.titlename too
|
||||
M_TextInputSetString(&demo.titlenameinput, demotitlename);
|
||||
Z_Free(title);
|
||||
}
|
||||
|
||||
// demo checksum
|
||||
demobuf.p += sizeof(UINT64);
|
||||
|
|
@ -4410,7 +4423,6 @@ void G_SaveDemo(void)
|
|||
|
||||
boolean G_DemoTitleResponder(event_t *ev)
|
||||
{
|
||||
size_t len;
|
||||
INT32 ch;
|
||||
|
||||
if (ev->type != ev_keydown)
|
||||
|
|
@ -4431,28 +4443,7 @@ boolean G_DemoTitleResponder(event_t *ev)
|
|||
return true;
|
||||
}
|
||||
|
||||
if ((ch >= HU_FONTSTART && ch <= HU_FONTEND && fontv[HU_FONT].font[ch-HU_FONTSTART])
|
||||
|| ch == ' ') // Allow spaces, of course
|
||||
{
|
||||
len = strlen(demo.titlename);
|
||||
if (len < 64)
|
||||
{
|
||||
demo.titlename[len+1] = 0;
|
||||
demo.titlename[len] = CON_ShiftChar(ch);
|
||||
}
|
||||
}
|
||||
else if (ch == KEY_BACKSPACE)
|
||||
{
|
||||
if (shiftdown)
|
||||
memset(demo.titlename, 0, sizeof(demo.titlename));
|
||||
else
|
||||
{
|
||||
len = strlen(demo.titlename);
|
||||
|
||||
if (len > 0)
|
||||
demo.titlename[len-1] = 0;
|
||||
}
|
||||
}
|
||||
M_TextInputHandle(&demo.titlenameinput, ch);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include "doomdef.h"
|
||||
#include "doomstat.h"
|
||||
#include "d_event.h"
|
||||
#include "m_textinput.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
@ -46,6 +47,7 @@ typedef enum
|
|||
// Publicly-accessible demo vars
|
||||
struct demovars_s {
|
||||
char titlename[65];
|
||||
textinput_t titlenameinput;
|
||||
boolean recording, playback, timing;
|
||||
UINT16 version; // Current file format of the demo being played
|
||||
boolean title; // Title Screen demo can be cancelled by any key
|
||||
|
|
|
|||
244
src/hu_stuff.c
244
src/hu_stuff.c
|
|
@ -44,6 +44,8 @@
|
|||
#include "p_local.h" // camera[]
|
||||
#include "p_tick.h"
|
||||
|
||||
#include "m_textinput.h"
|
||||
|
||||
#ifdef HWRENDER
|
||||
#include "hardware/hw_main.h"
|
||||
#endif
|
||||
|
|
@ -87,8 +89,8 @@ patch_t *frameslash; // framerate stuff. Used in screen.c
|
|||
static player_t *plr;
|
||||
boolean hu_keystrokes; // :)
|
||||
boolean chat_on; // entering a chat message?
|
||||
static char w_chat[HU_MAXMSGLEN + 1];
|
||||
static size_t c_input = 0; // let's try to make the chat input less shitty.
|
||||
static char w_chat_buf[HU_MAXMSGLEN + 1];
|
||||
static textinput_t w_chat;
|
||||
static boolean headsupactive = false;
|
||||
boolean hu_showscores; // draw rankings
|
||||
static char hu_tick;
|
||||
|
|
@ -920,71 +922,6 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
|
|||
#endif
|
||||
}
|
||||
|
||||
// Handles key input and string input
|
||||
//
|
||||
/*static inline boolean HU_keyInChatString(char *s, char ch)
|
||||
{
|
||||
size_t l;
|
||||
|
||||
if ((ch >= HU_FONTSTART && ch <= HU_FONTEND && fontv[HU_FONT].font[ch-HU_FONTSTART])
|
||||
|| ch == ' ') // Allow spaces, of course
|
||||
{
|
||||
l = strlen(s);
|
||||
if (l < HU_MAXMSGLEN - 1)
|
||||
{
|
||||
if (c_input >= strlen(s)) // don't do anything complicated
|
||||
{
|
||||
s[l++] = ch;
|
||||
s[l]=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// move everything past c_input for new characters:
|
||||
size_t m = HU_MAXMSGLEN-1;
|
||||
while (m>=c_input)
|
||||
{
|
||||
if (s[m])
|
||||
s[m+1] = (s[m]);
|
||||
if (m == 0) // prevent overflow
|
||||
break;
|
||||
m--;
|
||||
}
|
||||
s[c_input] = ch; // and replace this.
|
||||
}
|
||||
c_input++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else if (ch == KEY_BACKSPACE)
|
||||
{
|
||||
size_t i = c_input;
|
||||
|
||||
if (c_input <= 0)
|
||||
return false;
|
||||
|
||||
if (!s[i-1])
|
||||
return false;
|
||||
|
||||
if (i >= strlen(s)-1)
|
||||
{
|
||||
s[strlen(s)-1] = 0;
|
||||
c_input--;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (; (i < HU_MAXMSGLEN); i++)
|
||||
{
|
||||
s[i-1] = s[i];
|
||||
}
|
||||
c_input--;
|
||||
}
|
||||
else if (ch != KEY_ENTER)
|
||||
return false; // did not eat key
|
||||
|
||||
return true; // ate the key
|
||||
}*/
|
||||
|
||||
//
|
||||
//
|
||||
static void HU_TickSongCredits(void)
|
||||
|
|
@ -1109,8 +1046,8 @@ static boolean HU_chatboxContainsOnlySpaces(void)
|
|||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; w_chat[i]; i++)
|
||||
if (w_chat[i] != ' ')
|
||||
for (i = 0; w_chat_buf[i]; i++)
|
||||
if (w_chat_buf[i] != ' ')
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
|
@ -1128,16 +1065,15 @@ static void HU_sendChatMessage(void)
|
|||
return;
|
||||
|
||||
// copy printable characters and terminating '\0' only.
|
||||
for (ci = 2; w_chat[ci-2]; ci++)
|
||||
for (ci = 2; w_chat_buf[ci-2]; ci++)
|
||||
{
|
||||
char c = w_chat[ci-2];
|
||||
char c = w_chat_buf[ci-2];
|
||||
if (c >= ' ' && !(c & 0x80))
|
||||
buf[ci] = c;
|
||||
};
|
||||
buf[ci] = '\0';
|
||||
|
||||
memset(w_chat, '\0', sizeof(w_chat));
|
||||
c_input = 0;
|
||||
M_TextInputClear(&w_chat);
|
||||
|
||||
// last minute mute check
|
||||
if (CHAT_MUTE)
|
||||
|
|
@ -1212,9 +1148,8 @@ static void HU_sendChatMessage(void)
|
|||
|
||||
void HU_clearChatChars(void)
|
||||
{
|
||||
memset(w_chat, '\0', sizeof(w_chat));
|
||||
M_TextInputClear(&w_chat);
|
||||
chat_on = false;
|
||||
c_input = 0;
|
||||
|
||||
I_UpdateMouseGrab();
|
||||
}
|
||||
|
|
@ -1256,7 +1191,8 @@ boolean HU_Responder(event_t *ev)
|
|||
&& netgame && !OLD_MUTE) // check for old chat mute, still let the players open the chat incase they want to scroll otherwise.
|
||||
{
|
||||
chat_on = true;
|
||||
w_chat[0] = 0;
|
||||
w_chat_buf[0] = 0;
|
||||
M_TextInputInit(&w_chat, w_chat_buf, sizeof w_chat_buf);
|
||||
teamtalk = false;
|
||||
chat_scrollmedown = true;
|
||||
typelines = 1;
|
||||
|
|
@ -1266,7 +1202,8 @@ boolean HU_Responder(event_t *ev)
|
|||
&& netgame && !OLD_MUTE)
|
||||
{
|
||||
chat_on = true;
|
||||
w_chat[0] = 0;
|
||||
w_chat_buf[0] = 0;
|
||||
M_TextInputInit(&w_chat, w_chat_buf, sizeof w_chat_buf);
|
||||
teamtalk = G_GametypeHasTeams(); // Don't teamtalk if we don't have teams.
|
||||
chat_scrollmedown = true;
|
||||
typelines = 1;
|
||||
|
|
@ -1290,39 +1227,14 @@ boolean HU_Responder(event_t *ev)
|
|||
&& !G_ControlBoundToKey(0, gc_talkkey, ev->data1, false))
|
||||
return false;
|
||||
|
||||
c = CON_ShiftChar(c);
|
||||
M_TextInputHandle(&w_chat, c);
|
||||
|
||||
// pasting. pasting is cool. chat is a bit limited, though :(
|
||||
if ((c == 'v' || c == 'V') && ctrldown)
|
||||
{
|
||||
const char *paste;
|
||||
size_t chatlen;
|
||||
size_t pastelen;
|
||||
|
||||
if (CHAT_MUTE)
|
||||
return true;
|
||||
|
||||
paste = I_ClipboardPaste();
|
||||
if (paste == NULL)
|
||||
return true;
|
||||
|
||||
chatlen = strlen(w_chat);
|
||||
pastelen = strlen(paste);
|
||||
if (chatlen+pastelen > HU_MAXMSGLEN)
|
||||
return true; // we can't paste this!!
|
||||
|
||||
memmove(&w_chat[c_input + pastelen], &w_chat[c_input], pastelen);
|
||||
memcpy(&w_chat[c_input], paste, pastelen); // copy all of that.
|
||||
c_input += pastelen;
|
||||
return true;
|
||||
}
|
||||
else if (c == KEY_ENTER)
|
||||
if (c == KEY_ENTER)
|
||||
{
|
||||
if (!CHAT_MUTE)
|
||||
HU_sendChatMessage();
|
||||
|
||||
chat_on = false;
|
||||
c_input = 0; // reset input cursor
|
||||
chat_scrollmedown = true; // you hit enter, so you might wanna autoscroll to see what you just sent. :)
|
||||
I_UpdateMouseGrab();
|
||||
}
|
||||
|
|
@ -1332,7 +1244,6 @@ boolean HU_Responder(event_t *ev)
|
|||
&& c >= NUMKEYS)) // If it's not a keyboard key, then the chat button is used as a toggle.
|
||||
{
|
||||
chat_on = false;
|
||||
c_input = 0; // reset input cursor
|
||||
I_UpdateMouseGrab();
|
||||
}
|
||||
else if ((c == KEY_UPARROW || c == KEY_MOUSEWHEELUP) && chat_scroll > 0 && !OLDCHAT) // CHAT SCROLLING YAYS!
|
||||
|
|
@ -1347,45 +1258,6 @@ boolean HU_Responder(event_t *ev)
|
|||
justscrolleddown = true;
|
||||
chat_scrolltime = 4;
|
||||
}
|
||||
else if (c == KEY_LEFTARROW && c_input != 0 && !OLDCHAT) // i said go back
|
||||
{
|
||||
if (ctrldown)
|
||||
c_input = M_JumpWordReverse(w_chat, c_input);
|
||||
else
|
||||
c_input--;
|
||||
}
|
||||
else if (c == KEY_RIGHTARROW && c_input < strlen(w_chat) && !OLDCHAT) // don't need to check for admin or w/e here since the chat won't ever contain anything if it's muted.
|
||||
{
|
||||
if (ctrldown)
|
||||
c_input += M_JumpWord(&w_chat[c_input]);
|
||||
else
|
||||
c_input++;
|
||||
}
|
||||
else if ((c >= HU_FONTSTART && c <= HU_FONTEND && fontv[HU_FONT].font[c-HU_FONTSTART])
|
||||
|| c == ' ') // Allow spaces, of course
|
||||
{
|
||||
if (CHAT_MUTE || strlen(w_chat) >= HU_MAXMSGLEN)
|
||||
return true;
|
||||
|
||||
memmove(&w_chat[c_input + 1], &w_chat[c_input], strlen(w_chat) - c_input + 1);
|
||||
w_chat[c_input] = c;
|
||||
c_input++;
|
||||
}
|
||||
else if (c == KEY_BACKSPACE)
|
||||
{
|
||||
if (CHAT_MUTE || c_input <= 0)
|
||||
return true;
|
||||
|
||||
memmove(&w_chat[c_input - 1], &w_chat[c_input], strlen(w_chat) - c_input + 1);
|
||||
c_input--;
|
||||
}
|
||||
else if (c == KEY_DEL)
|
||||
{
|
||||
if (CHAT_MUTE || c_input >= strlen(w_chat))
|
||||
return true;
|
||||
|
||||
memmove(&w_chat[c_input], &w_chat[c_input + 1], strlen(w_chat) - c_input);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1742,8 +1614,9 @@ static void HU_DrawChat(void)
|
|||
INT32 charwidth = 4, charheight = 6;
|
||||
INT32 boxw = cv_chatwidth.value;
|
||||
INT32 t = 0, c = 0, y = chaty - (typelines*charheight);
|
||||
UINT32 i = 0, saylen = strlen(w_chat); // You learn new things everyday!
|
||||
INT32 cflag = 0;
|
||||
UINT32 i = 0, saylen = strlen(w_chat_buf); // You learn new things everyday!
|
||||
INT32 cflag = 0;\
|
||||
size_t select_start = 0, select_end = 0;
|
||||
const char *ntalk = "Say: ", *ttalk = "Team: ";
|
||||
const char *talk = ntalk;
|
||||
const char *mute = "Chat has been muted.";
|
||||
|
|
@ -1807,13 +1680,19 @@ static void HU_DrawChat(void)
|
|||
i = 0;
|
||||
typelines = 1;
|
||||
|
||||
if ((strlen(w_chat) == 0 || c_input == 0) && hu_tick < 4)
|
||||
V_DrawChatCharacter(chatx + 2 + c, y+1, '_' |V_SNAPTOBOTTOM|V_SNAPTOLEFT|t, true, NULL);
|
||||
if ((w_chat.cursor == 0 || w_chat.length == 0) && hu_tick < 4)
|
||||
V_DrawChatCharacter(chatx+2+c+charwidth*w_chat.cursor, y+1, '_'|V_SNAPTOBOTTOM|V_SNAPTOLEFT|t, !cv_menucaps.value, NULL);
|
||||
|
||||
while (w_chat[i])
|
||||
if (w_chat.select != w_chat.cursor)
|
||||
{
|
||||
select_start = min(w_chat.select, w_chat.cursor);
|
||||
select_end = max(w_chat.select, w_chat.cursor);
|
||||
}
|
||||
|
||||
while (w_chat_buf[i])
|
||||
{
|
||||
boolean skippedline = false;
|
||||
if (c_input == (i+1))
|
||||
if (w_chat.cursor == (i+1))
|
||||
{
|
||||
INT32 cursorx = (c+charwidth < boxw-charwidth) ? (chatx + 2 + c+charwidth) : (chatx+1); // we may have to go down.
|
||||
INT32 cursory = (cursorx != chatx+1) ? (y) : (y+charheight);
|
||||
|
|
@ -1828,10 +1707,13 @@ static void HU_DrawChat(void)
|
|||
}
|
||||
|
||||
//Hurdler: isn't it better like that?
|
||||
if (w_chat[i] < HU_FONTSTART)
|
||||
++i;
|
||||
else
|
||||
V_DrawChatCharacter(chatx + c + 2, y, w_chat[i++] | V_SNAPTOBOTTOM|V_SNAPTOLEFT | t, true, NULL);
|
||||
if (w_chat_buf[i] >= HU_FONTSTART)
|
||||
V_DrawChatCharacter(chatx + c + 2, y, w_chat_buf[i] | V_SNAPTOBOTTOM|V_SNAPTOLEFT | t, !cv_menucaps.value, NULL);
|
||||
|
||||
// Draw selection
|
||||
if (i >= select_start && i < select_end)
|
||||
V_DrawFill(chatx + c + 2, y-1, charwidth, charheight, 103|V_TRANSLUCENT|V_SNAPTOBOTTOM|V_SNAPTOLEFT|t);
|
||||
++i;
|
||||
|
||||
c += charwidth;
|
||||
if (c > boxw-(charwidth*2) && !skippedline)
|
||||
|
|
@ -1843,7 +1725,7 @@ static void HU_DrawChat(void)
|
|||
}
|
||||
|
||||
// handle /pm list. It's messy, horrible and I don't care.
|
||||
if (strnicmp(w_chat, "/pm", 3) == 0 && vid.width >= 400 && !teamtalk) // 320x200 unsupported kthxbai
|
||||
if (strnicmp(w_chat_buf, "/pm", 3) == 0 && vid.width >= 400 && !teamtalk) // 320x200 unsupported kthxbai
|
||||
{
|
||||
INT32 count = 0;
|
||||
INT32 p_dispy = chaty - charheight -1;
|
||||
|
|
@ -1864,34 +1746,34 @@ static void HU_DrawChat(void)
|
|||
for(i=0; (i<MAXPLAYERS); i++)
|
||||
{
|
||||
// filter: (code needs optimization pls help I'm bad with C)
|
||||
if (w_chat[3])
|
||||
if (w_chat_buf[3])
|
||||
{
|
||||
char playernum[3];
|
||||
UINT32 n;
|
||||
// right, that's half important: (w_chat[4] may be a space since /pm0 msg is perfectly acceptable!)
|
||||
if ( ( ((w_chat[3] != 0) && ((w_chat[3] < '0') || (w_chat[3] > '9'))) || ((w_chat[4] != 0) && (((w_chat[4] < '0') || (w_chat[4] > '9'))))) && (w_chat[4] != ' '))
|
||||
// right, that's half important: (w_chat_buf[4] may be a space since /pm0 msg is perfectly acceptable!)
|
||||
if ( ( ((w_chat_buf[3] != 0) && ((w_chat_buf[3] < '0') || (w_chat_buf[3] > '9'))) || ((w_chat_buf[4] != 0) && (((w_chat_buf[4] < '0') || (w_chat_buf[4] > '9'))))) && (w_chat_buf[4] != ' '))
|
||||
break;
|
||||
|
||||
strncpy(playernum, w_chat+3, 3);
|
||||
strncpy(playernum, w_chat_buf+3, 3);
|
||||
n = atoi(playernum); // turn that into a number
|
||||
// special cases:
|
||||
|
||||
if ((n == 0) && !(w_chat[4] == '0'))
|
||||
if ((n == 0) && !(w_chat_buf[4] == '0'))
|
||||
{
|
||||
if (!(i<10))
|
||||
continue;
|
||||
}
|
||||
else if ((n == 1) && !(w_chat[3] == '0'))
|
||||
else if ((n == 1) && !(w_chat_buf[3] == '0'))
|
||||
{
|
||||
if (!((i == 1) || ((i >= 10) && (i <= 19))))
|
||||
continue;
|
||||
}
|
||||
else if ((n == 2) && !(w_chat[3] == '0'))
|
||||
else if ((n == 2) && !(w_chat_buf[3] == '0'))
|
||||
{
|
||||
if (!((i == 2) || ((i >= 20) && (i <= 29))))
|
||||
continue;
|
||||
}
|
||||
else if ((n == 3) && !(w_chat[3] == '0'))
|
||||
else if ((n == 3) && !(w_chat_buf[3] == '0'))
|
||||
{
|
||||
if (!((i == 3) || ((i >= 30) && (i <= 31))))
|
||||
continue;
|
||||
|
|
@ -1932,6 +1814,7 @@ static void HU_DrawChat_Old(void)
|
|||
size_t i = 0;
|
||||
const char *ntalk = "Say: ", *ttalk = "Say-Team: ";
|
||||
const char *talk = ntalk;
|
||||
size_t select_start = 0, select_end = 0;
|
||||
INT32 charwidth = 8 * con_scalefactor; //(fontv[HU_FONT].font['A'-HU_FONTSTART]->width) * con_scalefactor;
|
||||
INT32 charheight = 8 * con_scalefactor; //(fontv[HU_FONT].font['A'-HU_FONTSTART]->height) * con_scalefactor;
|
||||
if (teamtalk)
|
||||
|
|
@ -1955,19 +1838,24 @@ static void HU_DrawChat_Old(void)
|
|||
else
|
||||
{
|
||||
//charwidth = (fontv[HU_FONT].font[talk[i]-HU_FONTSTART]->width) * con_scalefactor;
|
||||
V_DrawCharacter(HU_INPUTX + c, y, talk[i++] | cv_constextsize.value | V_NOSCALESTART, true);
|
||||
V_DrawCharacter(HU_INPUTX + c, y, talk[i++] | cv_constextsize.value | V_NOSCALESTART, !cv_menucaps.value);
|
||||
}
|
||||
c += charwidth;
|
||||
}
|
||||
|
||||
if ((strlen(w_chat) == 0 || c_input == 0) && hu_tick < 4)
|
||||
V_DrawCharacter(HU_INPUTX+c, y+2*con_scalefactor, '_' |cv_constextsize.value | V_NOSCALESTART|t, true);
|
||||
if ((w_chat.cursor == 0 || w_chat.length == 0) && hu_tick < 4)
|
||||
V_DrawCharacter(HU_INPUTX+c+charwidth*w_chat.cursor, y+2*con_scalefactor, '_'|cv_constextsize.value|V_NOSCALESTART|t, !cv_menucaps.value);
|
||||
|
||||
if (w_chat.select != w_chat.cursor)
|
||||
{
|
||||
select_start = min(w_chat.select, w_chat.cursor);
|
||||
select_end = max(w_chat.select, w_chat.cursor);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while (w_chat[i])
|
||||
while (w_chat_buf[i])
|
||||
{
|
||||
|
||||
if (c_input == (i+1) && hu_tick < 4)
|
||||
if (w_chat.cursor == (i+1) && hu_tick < 4)
|
||||
{
|
||||
INT32 cursorx = (HU_INPUTX+c+charwidth < vid.width) ? (HU_INPUTX + c + charwidth) : (HU_INPUTX); // we may have to go down.
|
||||
INT32 cursory = (cursorx != HU_INPUTX) ? (y) : (y+charheight);
|
||||
|
|
@ -1975,17 +1863,18 @@ static void HU_DrawChat_Old(void)
|
|||
}
|
||||
|
||||
//Hurdler: isn't it better like that?
|
||||
if (w_chat[i] < HU_FONTSTART)
|
||||
{
|
||||
++i;
|
||||
//charwidth = 4 * con_scalefactor;
|
||||
}
|
||||
else
|
||||
if (w_chat_buf[i] >= HU_FONTSTART)
|
||||
{
|
||||
//charwidth = (fontv[HU_FONT].font[w_chat[i]-HU_FONTSTART]->width) * con_scalefactor;
|
||||
V_DrawCharacter(HU_INPUTX + c, y, w_chat[i++] | cv_constextsize.value | V_NOSCALESTART | t, true);
|
||||
V_DrawCharacter(HU_INPUTX + c, y, w_chat_buf[i] | cv_constextsize.value | V_NOSCALESTART | t, !cv_menucaps.value);
|
||||
}
|
||||
|
||||
// Draw selection
|
||||
if (i >= select_start && i < select_end)
|
||||
V_DrawFill(HU_INPUTX + c, y, charwidth, charheight, 103|V_TRANSLUCENT|cv_constextsize.value|V_NOSCALESTART|t);
|
||||
|
||||
++i;
|
||||
|
||||
c += charwidth;
|
||||
if (c >= vid.width)
|
||||
{
|
||||
|
|
@ -1993,9 +1882,6 @@ static void HU_DrawChat_Old(void)
|
|||
y += charheight;
|
||||
}
|
||||
}
|
||||
|
||||
if (hu_tick < 4)
|
||||
V_DrawCharacter(HU_INPUTX + c, y, '_' | cv_constextsize.value |V_NOSCALESTART|t, true);
|
||||
}
|
||||
|
||||
static void HU_DrawCEcho(void)
|
||||
|
|
|
|||
|
|
@ -1824,8 +1824,13 @@ void LUA_Step(void)
|
|||
{
|
||||
if (!gL)
|
||||
return;
|
||||
lua_settop(gL, 0);
|
||||
lua_gc(gL, LUA_GCSTEP, 1);
|
||||
|
||||
if (lua_gettop(gL) != 0)
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, "Eek, there is garbage on lua stack!\n");
|
||||
lua_settop(gL, 0);
|
||||
lua_gc(gL, LUA_GCSTEP, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void LUA_Archive(savebuffer_t *save, boolean network)
|
||||
|
|
|
|||
233
src/m_menu.c
233
src/m_menu.c
|
|
@ -30,7 +30,9 @@
|
|||
#include "hu_stuff.h"
|
||||
#include "g_game.h"
|
||||
#include "g_input.h"
|
||||
#include "v_video.h"
|
||||
#include "m_argv.h"
|
||||
#include "m_textinput.h"
|
||||
|
||||
// Data.
|
||||
#include "sounds.h"
|
||||
|
|
@ -142,6 +144,9 @@ const char *quitmsg[NUM_QUITMESSAGES];
|
|||
|
||||
boolean fromlevelselect = false;
|
||||
|
||||
static char menu_text_input_buf[MAXSTRINGLENGTH];
|
||||
static textinput_t menuinput;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
LLM_CREATESERVER,
|
||||
|
|
@ -255,9 +260,25 @@ static INT16 M_GetMenuIndex(menutype_t type, const char *name)
|
|||
return M_GetMenuItem(type, name) - menudefs[type].menuitems;
|
||||
}
|
||||
|
||||
static boolean M_ItemSelectable(menuitem_t *item)
|
||||
{
|
||||
return (item->status & IT_INTERACT) && !(item->status & (IT_HIDDEN|IT_GRAYEDOUT|IT_SECRET));
|
||||
}
|
||||
|
||||
// TODO this ought to be controlled by the item argument...
|
||||
static UINT32 M_StringCvarLength(consvar_t *cv)
|
||||
{
|
||||
if (cv == &cv_dummyip)
|
||||
return 28;
|
||||
else if (cv == &cv_dummyname || cv == &cv_playername[0] || cv == &cv_playername[1] || cv == &cv_playername[2] || cv == &cv_playername[3])
|
||||
return MAXPLAYERNAME + 1;
|
||||
else
|
||||
return MAXSTRINGLENGTH;
|
||||
}
|
||||
|
||||
// an array of macros for getting/setting menuitem properties
|
||||
#define M_IsItemOn(t, n) (itemOn == M_GetMenuIndex(t, n))
|
||||
#define M_SetItemOn(t, n) (itemOn = M_GetMenuIndex(t, n))
|
||||
#define M_SetItemOn(t, n) (M_RawSetItemOn(&menudefs[t], M_GetMenuIndex(t, n)))
|
||||
#define M_SetItemRoutine(t, n, v) (M_GetMenuItem(t, n)->routine = v)
|
||||
#define M_SetItemCvar(t, n, v) (M_GetMenuItem(t, n)->cvar = v)
|
||||
#define M_SetItemArgument(t, n, v) (M_GetMenuItem(t, n)->argument = v)
|
||||
|
|
@ -266,6 +287,41 @@ static INT16 M_GetMenuIndex(menutype_t type, const char *name)
|
|||
#define M_GetItemX(t, n) (M_GetMenuItem(t, n)->x)
|
||||
#define M_GetItemY(t, n) (M_GetMenuItem(t, n)->y)
|
||||
|
||||
static void M_RawSetItemOn(menu_t *menu, INT16 index)
|
||||
{
|
||||
INT16 i;
|
||||
itemOn = index;
|
||||
|
||||
if (!menu->numitems)
|
||||
return;
|
||||
|
||||
// in case of...
|
||||
if (itemOn >= menu->numitems)
|
||||
itemOn = menu->numitems - 1;
|
||||
|
||||
// the curent item can be disabled,
|
||||
// this code go up until an enabled item found
|
||||
if (menu->numitems && !M_ItemSelectable(&menu->menuitems[itemOn]))
|
||||
{
|
||||
for (i = 0; i < menu->numitems; i++)
|
||||
{
|
||||
if (M_ItemSelectable(&menu->menuitems[i]))
|
||||
{
|
||||
itemOn = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update text input for string cvars
|
||||
consvar_t *cv = menu->menuitems[itemOn].cvar;
|
||||
if (cv && !cv->PossibleValue)
|
||||
{
|
||||
M_TextInputInit(&menuinput, menu_text_input_buf, M_StringCvarLength(cv));
|
||||
M_TextInputSetString(&menuinput, cv->string);
|
||||
}
|
||||
}
|
||||
|
||||
static void M_ChangeItemStatus(menutype_t type, const char *name, menuitemflags_t flag, boolean cond)
|
||||
{
|
||||
if (cond)
|
||||
|
|
@ -294,11 +350,6 @@ static void M_SetItemPatch(menutype_t type, const char *name, const char *string
|
|||
#define M_SetItemDisabled(t, n, c) M_ChangeItemStatus(t, n, IT_GRAYEDOUT, c)
|
||||
#define M_SetItemSecret(t, n, c) M_ChangeItemStatus(t, n, IT_SECRET, c)
|
||||
|
||||
static boolean M_ItemSelectable(menuitem_t *item)
|
||||
{
|
||||
return (item->status & IT_INTERACT) && !(item->status & (IT_HIDDEN|IT_GRAYEDOUT|IT_SECRET));
|
||||
}
|
||||
|
||||
// bruh...
|
||||
static UINT32 M_ServersPerPage(void)
|
||||
{
|
||||
|
|
@ -1115,76 +1166,6 @@ static void M_ChangeCvar(menuitem_t *item, SINT8 direction)
|
|||
M_GetFollowerState(); // update follower state
|
||||
}
|
||||
|
||||
static UINT32 M_StringCvarLength(consvar_t *cv)
|
||||
{
|
||||
if (cv == &cv_dummyip)
|
||||
return 28;
|
||||
else if (cv == &cv_dummyname || cv == &cv_playername[0] || cv == &cv_playername[1] || cv == &cv_playername[2] || cv == &cv_playername[3])
|
||||
return MAXPLAYERNAME + 1;
|
||||
else
|
||||
return MAXSTRINGLENGTH;
|
||||
}
|
||||
|
||||
static boolean M_ChangeStringCvar(INT32 choice)
|
||||
{
|
||||
consvar_t *cv = currentMenu->menuitems[itemOn].cvar;
|
||||
char buf[MAXSTRINGLENGTH];
|
||||
size_t len = strlen(cv->string);
|
||||
|
||||
if (shiftdown && choice >= 32 && choice <= 127)
|
||||
choice = shiftxform[choice];
|
||||
|
||||
switch (choice)
|
||||
{
|
||||
case KEY_BACKSPACE:
|
||||
if (len > 0)
|
||||
{
|
||||
S_StartSound(NULL,sfx_menu1); // Tails
|
||||
M_Memcpy(buf, cv->string, len);
|
||||
buf[len-1] = 0;
|
||||
CV_Set(cv, buf);
|
||||
}
|
||||
return true;
|
||||
case KEY_DEL:
|
||||
if (cv->string[0])
|
||||
{
|
||||
S_StartSound(NULL,sfx_menu1); // Tails
|
||||
CV_Set(cv, "");
|
||||
}
|
||||
return true;
|
||||
case KEY_LEFTARROW:
|
||||
case KEY_RIGHTARROW:
|
||||
// TODO: navigation
|
||||
return true;
|
||||
default:
|
||||
if (cv == &cv_dummyip)
|
||||
{
|
||||
// Rudimentary number and period enforcing - also allows letters so hostnames can be used instead
|
||||
if (!((choice >= '-' && choice <= ':') || (choice >= 'A' && choice <= 'Z') || (choice >= 'a' && choice <= 'z'))
|
||||
&& !(choice >= KEY_KEYPAD7 && choice <= KEY_KPADDEL && choice != KEY_MINUSPAD && choice != KEY_PLUSPAD)) //numpad too!
|
||||
break;
|
||||
|
||||
if (choice >= KEY_KEYPAD7)
|
||||
// sir, have you been golfing tonight?
|
||||
choice = "789-456+1230."[choice - KEY_KEYPAD7];
|
||||
}
|
||||
|
||||
if (choice < 32 || choice > 127)
|
||||
break;
|
||||
|
||||
if (len < M_StringCvarLength(cv) - 1)
|
||||
{
|
||||
S_StartSound(NULL, sfx_menu1); // Tails
|
||||
M_Memcpy(buf, cv->string, len);
|
||||
buf[len++] = (char)choice;
|
||||
buf[len] = 0;
|
||||
CV_Set(cv, buf);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// lock out further input in a tic when important buttons are pressed
|
||||
// (in other words -- stop bullshit happening by mashing buttons in fades)
|
||||
static INT32 noFurtherInput = 0;
|
||||
|
|
@ -1195,7 +1176,7 @@ static void Command_Manual_f(void)
|
|||
return;
|
||||
M_StartControlPanel();
|
||||
M_EnterMenu(MN_HELP, true, 0);
|
||||
itemOn = 0;
|
||||
M_SetItemOn(MN_HELP, "PAGE0");
|
||||
}
|
||||
|
||||
// arbitrary keyboard shortcuts because fuck you
|
||||
|
|
@ -1502,7 +1483,7 @@ boolean M_Responder(event_t *ev)
|
|||
M_StartControlPanel();
|
||||
M_EnterMenu(MN_OP_MAIN, true, 0);
|
||||
M_EnterMenu(MN_OP_SOUND, true, 0);
|
||||
itemOn = 0;
|
||||
M_SetItemOn(MN_OP_SOUND, "SOUND");
|
||||
return true;
|
||||
|
||||
case KEY_F5: // Video Mode
|
||||
|
|
@ -1572,8 +1553,13 @@ boolean M_Responder(event_t *ev)
|
|||
{
|
||||
if (item->cvar->flags & CV_CALL && M_WipeBuffer(ch, MR_Dummy))
|
||||
return true;
|
||||
if (M_ChangeStringCvar(ch))
|
||||
|
||||
if (M_TextInputHandle(&menuinput, ch))
|
||||
{
|
||||
S_StartSound(NULL, sfx_menu1); // Tails
|
||||
CV_Set(item->cvar, menuinput.buffer);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (menustack[0] == MN_PLAYBACK && !con_destlines)
|
||||
|
|
@ -1594,6 +1580,7 @@ boolean M_Responder(event_t *ev)
|
|||
}
|
||||
|
||||
INT16 oldItemOn = itemOn; // prevent infinite loop
|
||||
INT16 newItemOn = itemOn;
|
||||
|
||||
// Keys usable within menu
|
||||
switch (ch)
|
||||
|
|
@ -1602,10 +1589,11 @@ boolean M_Responder(event_t *ev)
|
|||
if (!item)
|
||||
return true;
|
||||
|
||||
do if (++itemOn >= currentMenu->numitems)
|
||||
itemOn = 0;
|
||||
while (oldItemOn != itemOn && !M_ItemSelectable(¤tMenu->menuitems[itemOn]));
|
||||
do if (++newItemOn >= currentMenu->numitems)
|
||||
newItemOn = 0;
|
||||
while (oldItemOn != newItemOn && !M_ItemSelectable(¤tMenu->menuitems[newItemOn]));
|
||||
|
||||
M_RawSetItemOn(currentMenu, newItemOn);
|
||||
S_StartSound(NULL, sfx_menu1);
|
||||
return true;
|
||||
|
||||
|
|
@ -1613,10 +1601,11 @@ boolean M_Responder(event_t *ev)
|
|||
if (!item)
|
||||
return true;
|
||||
|
||||
do if (--itemOn < 0)
|
||||
itemOn = currentMenu->numitems - 1;
|
||||
while (oldItemOn != itemOn && !M_ItemSelectable(¤tMenu->menuitems[itemOn]));
|
||||
do if (--newItemOn < 0)
|
||||
newItemOn = currentMenu->numitems - 1;
|
||||
while (oldItemOn != newItemOn && !M_ItemSelectable(¤tMenu->menuitems[newItemOn]));
|
||||
|
||||
M_RawSetItemOn(currentMenu, newItemOn);
|
||||
S_StartSound(NULL, sfx_menu1);
|
||||
return true;
|
||||
|
||||
|
|
@ -1933,7 +1922,6 @@ void M_ClearMenus(boolean callexitmenufunc)
|
|||
//
|
||||
static void M_SetupNextMenu(menutype_t menunum, boolean callexit)
|
||||
{
|
||||
INT16 i;
|
||||
menu_t *menudef = &menudefs[menunum];
|
||||
|
||||
// If you're going from a menu to itself, why are you running the quitroutine? You're not quitting it! -SH
|
||||
|
|
@ -1943,30 +1931,9 @@ static void M_SetupNextMenu(menutype_t menunum, boolean callexit)
|
|||
M_HandleMenuPresState(menunum);
|
||||
|
||||
currentMenu = menudef;
|
||||
itemOn = currentMenu->lastOn;
|
||||
M_RawSetItemOn(currentMenu, currentMenu->lastOn);
|
||||
|
||||
hidetitlemap = false;
|
||||
|
||||
if (!currentMenu->numitems)
|
||||
return;
|
||||
|
||||
// in case of...
|
||||
if (itemOn >= currentMenu->numitems)
|
||||
itemOn = currentMenu->numitems - 1;
|
||||
|
||||
// the curent item can be disabled,
|
||||
// this code go up until an enabled item found
|
||||
if (!M_ItemSelectable(¤tMenu->menuitems[itemOn]))
|
||||
{
|
||||
for (i = 0; i < currentMenu->numitems; i++)
|
||||
{
|
||||
if (M_ItemSelectable(¤tMenu->menuitems[i]))
|
||||
{
|
||||
itemOn = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pop the active menu from the stack
|
||||
|
|
@ -2260,7 +2227,23 @@ void M_DrawTextBox(INT32 x, INT32 y, INT32 width, INT32 boxlines)
|
|||
{
|
||||
// Solid color textbox.
|
||||
V_DrawFill(x+5, y+5, width*8+6, boxlines*8+6, 159);
|
||||
//V_DrawFill(x+8, y+8, width*8, boxlines*8, 31);
|
||||
}
|
||||
|
||||
void M_DrawTextInput(INT32 x, INT32 y, INT32 flags, textinput_t *input)
|
||||
{
|
||||
// draw text cursor for name
|
||||
if (skullAnimCounter < 4) // blink cursor
|
||||
V_DrawCharacter(x+V_SubStringWidth(input->buffer, input->cursor, flags|V_ALLOWLOWERCASE), y+3, '_'|(flags & ~(V_FLIP|V_PARAMMASK)), false);
|
||||
|
||||
// draw selection
|
||||
if (input->select != input->cursor)
|
||||
{
|
||||
size_t start = min(input->select, input->cursor);
|
||||
size_t end = max(input->select, input->cursor);
|
||||
size_t len = end - start;
|
||||
INT32 startx = V_SubStringWidth(input->buffer, start, flags|V_ALLOWLOWERCASE);
|
||||
V_DrawFill(x+startx, y, V_SubStringWidth(input->buffer+start, len, V_ALLOWLOWERCASE), 8, (flags & ~(V_ALPHAMASK|V_PARAMMASK))|103|V_TRANSLUCENT);
|
||||
}
|
||||
}
|
||||
|
||||
// horizontally centered text
|
||||
|
|
@ -2369,9 +2352,8 @@ static void M_DrawRightString(menuitem_t *item, INT16 x, INT16 y, INT32 vflags,
|
|||
|
||||
M_DrawTextBox(x + xofs, y + yofs, w, 1);
|
||||
V_DrawString(x + xofs + 8, y + yofs + 8, vflags|V_ALLOWLOWERCASE, cv->string);
|
||||
if (selected && skullAnimCounter < 4) // blink cursor
|
||||
V_DrawCharacter(x + xofs + 8 + V_StringWidth(cv->string, vflags|V_ALLOWLOWERCASE), y + yofs + 8,
|
||||
'_' | (vflags & ~(V_FLIP|V_PARAMMASK)), false);
|
||||
if (selected)
|
||||
M_DrawTextInput(x + xofs + 8, y + yofs + 8, vflags|V_ALLOWLOWERCASE, &menuinput);
|
||||
}
|
||||
else if (item->status & IT_SLIDER)
|
||||
{
|
||||
|
|
@ -2526,7 +2508,7 @@ static INT32 M_DrawMenuItem(menuitem_t *item, INT16 x, INT16 y, INT32 vflags, bo
|
|||
fixed_t scale = item->status & IT_SMALL ? FRACUNIT/2 : FRACUNIT;
|
||||
int font;
|
||||
INT32 colorflag;
|
||||
INT32 (*widthfunc)(const char *string, INT32 option);
|
||||
INT32 (*widthfunc)(const char *string, INT32 length, INT32 option);
|
||||
|
||||
if (item->status & IT_SECRET)
|
||||
{
|
||||
|
|
@ -2609,24 +2591,24 @@ static INT32 M_DrawMenuItem(menuitem_t *item, INT16 x, INT16 y, INT32 vflags, bo
|
|||
vflags |= V_6WIDTHSPACE; // FALLTHRU
|
||||
case ITF_THIN:
|
||||
font = TINY_FONT;
|
||||
widthfunc = V_ThinStringWidth;
|
||||
widthfunc = V_ThinSubStringWidth;
|
||||
break;
|
||||
|
||||
case ITF_HEADER:
|
||||
x -= 16; // FALLTHRU
|
||||
default:
|
||||
font = HU_FONT;
|
||||
widthfunc = V_StringWidth;
|
||||
widthfunc = V_SubStringWidth;
|
||||
break;
|
||||
}
|
||||
|
||||
if (item->status & IT_CENTER)
|
||||
width = widthfunc(string, vflags)/2;
|
||||
width = widthfunc(string, -1, vflags)/2;
|
||||
else if (item->status & IT_RIGHT)
|
||||
width = widthfunc(string, vflags);
|
||||
width = widthfunc(string, -1, vflags);
|
||||
|
||||
if (item->status & IT_STICKER)
|
||||
M_DrawSticker(x, y + 2, widthfunc(string, vflags), vflags & ~V_FLIP, true);
|
||||
M_DrawSticker(x, y + 2, widthfunc(string, -1, vflags), vflags & ~V_FLIP, true);
|
||||
|
||||
V_DrawStringScaled((x - width)<<FRACBITS, y<<FRACBITS, scale, FRACUNIT, FRACUNIT, (selected ? highlightflags : colorflag)|vflags, font, string);
|
||||
|
||||
|
|
@ -3195,15 +3177,14 @@ INT32 MR_HandleImageDef(INT32 choice)
|
|||
if (itemOn >= (INT16)(currentMenu->numitems-1))
|
||||
break;
|
||||
S_StartSound(NULL, sfx_menu1);
|
||||
itemOn++;
|
||||
M_RawSetItemOn(currentMenu, itemOn + 1);
|
||||
break;
|
||||
|
||||
case KEY_LEFTARROW:
|
||||
if (!itemOn)
|
||||
break;
|
||||
|
||||
S_StartSound(NULL, sfx_menu1);
|
||||
itemOn--;
|
||||
M_RawSetItemOn(currentMenu, itemOn - 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -6370,7 +6351,7 @@ static void M_ConnectMenu(INT32 choice)
|
|||
// first page of servers
|
||||
CV_SetValue(&cv_dummyserverpage, 0);
|
||||
M_EnterMenu(MN_MP_CONNECT, true, 0);
|
||||
itemOn = 0;
|
||||
M_SetItemOn(MN_MP_CONNECT, "SORTING");
|
||||
|
||||
#if defined (MASTERSERVER) && defined (HAVE_THREADS)
|
||||
I_lock_mutex(&ms_QueryId_mutex);
|
||||
|
|
@ -8006,7 +7987,7 @@ INT32 MR_HandleMonitorToggles(INT32 choice)
|
|||
if (((column*height)+row) >= currentMenu->numitems)
|
||||
column = 0;
|
||||
next = min(((column*height)+row), currentMenu->numitems-1);
|
||||
itemOn = next;
|
||||
M_RawSetItemOn(currentMenu, next);
|
||||
break;
|
||||
|
||||
case KEY_LEFTARROW:
|
||||
|
|
@ -8019,7 +8000,7 @@ INT32 MR_HandleMonitorToggles(INT32 choice)
|
|||
next = max(((column*height)+row), 0);
|
||||
if (next >= currentMenu->numitems)
|
||||
next = currentMenu->numitems-1;
|
||||
itemOn = next;
|
||||
M_RawSetItemOn(currentMenu, next);
|
||||
break;
|
||||
|
||||
case KEY_DOWNARROW:
|
||||
|
|
@ -8028,7 +8009,7 @@ INT32 MR_HandleMonitorToggles(INT32 choice)
|
|||
if (((column*height)+row) >= currentMenu->numitems)
|
||||
row = 0;
|
||||
next = min(((column*height)+row), currentMenu->numitems-1);
|
||||
itemOn = next;
|
||||
M_RawSetItemOn(currentMenu, next);
|
||||
break;
|
||||
|
||||
case KEY_UPARROW:
|
||||
|
|
@ -8041,7 +8022,7 @@ INT32 MR_HandleMonitorToggles(INT32 choice)
|
|||
next = max(((column*height)+row), 0);
|
||||
if (next >= currentMenu->numitems)
|
||||
next = currentMenu->numitems-1;
|
||||
itemOn = next;
|
||||
M_RawSetItemOn(currentMenu, next);
|
||||
break;
|
||||
|
||||
case KEY_ENTER:
|
||||
|
|
|
|||
308
src/m_textinput.c
Normal file
308
src/m_textinput.c
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
#include "m_textinput.h"
|
||||
#include "i_system.h"
|
||||
#include "keys.h"
|
||||
#include "console.h"
|
||||
|
||||
static void M_TextInputDel(textinput_t *input, size_t start, size_t end)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
len = (end - start);
|
||||
|
||||
if (end != input->length)
|
||||
memmove(&input->buffer[start], &input->buffer[end], input->length-end);
|
||||
memset(&input->buffer[input->length - len], 0, len);
|
||||
|
||||
input->length -= len;
|
||||
|
||||
if (input->select >= end)
|
||||
input->select -= len;
|
||||
else if (input->select > start)
|
||||
input->select = start;
|
||||
|
||||
if (input->cursor >= end)
|
||||
input->cursor -= len;
|
||||
else if (input->cursor > start)
|
||||
input->cursor = start;
|
||||
}
|
||||
|
||||
static void M_TextInputDelSelection(textinput_t *input)
|
||||
{
|
||||
size_t start, end;
|
||||
|
||||
if (input->cursor > input->select)
|
||||
{
|
||||
start = input->select;
|
||||
end = input->cursor;
|
||||
}
|
||||
else
|
||||
{
|
||||
start = input->cursor;
|
||||
end = input->select;
|
||||
}
|
||||
|
||||
M_TextInputDel(input, start, end);
|
||||
|
||||
input->select = input->cursor = start;
|
||||
}
|
||||
|
||||
static void M_TextInputAddString(textinput_t *input, const char *c)
|
||||
{
|
||||
size_t csize = strlen(c);
|
||||
|
||||
if (input->length + csize > input->buffer_size-1)
|
||||
return;
|
||||
|
||||
if (input->cursor != input->length)
|
||||
memmove(&input->buffer[input->cursor+csize], &input->buffer[input->cursor], input->length-input->cursor);
|
||||
memcpy(&input->buffer[input->cursor], c, csize);
|
||||
input->length += csize;
|
||||
input->select = (input->cursor += csize);
|
||||
input->buffer[input->length] = 0;
|
||||
}
|
||||
|
||||
static void M_TextInputAddChar(textinput_t *input, char c)
|
||||
{
|
||||
if (input->length >= input->buffer_size-1)
|
||||
return;
|
||||
|
||||
if (input->cursor != input->length)
|
||||
memmove(&input->buffer[input->cursor+1], &input->buffer[input->cursor], input->length-input->cursor);
|
||||
input->buffer[input->cursor++] = c;
|
||||
input->buffer[++input->length] = 0;
|
||||
input->select = input->cursor;
|
||||
}
|
||||
|
||||
static void M_TextInputDelChar(textinput_t *input)
|
||||
{
|
||||
if (!input->cursor)
|
||||
return;
|
||||
|
||||
if (input->cursor != input->length)
|
||||
memmove(&input->buffer[input->cursor-1], &input->buffer[input->cursor], input->length-input->cursor);
|
||||
input->buffer[--input->length] = 0;
|
||||
input->select = --input->cursor;
|
||||
}
|
||||
|
||||
static void M_TextInputToWordEnd(textinput_t *input, boolean move_sel)
|
||||
{
|
||||
// Skip spaces
|
||||
while (input->cursor < input->length && isspace(input->buffer[input->cursor]))
|
||||
++input->cursor;
|
||||
|
||||
// Skip word
|
||||
while (input->cursor < input->length && !isspace(input->buffer[input->cursor]))
|
||||
++input->cursor;
|
||||
|
||||
if (move_sel) input->select = input->cursor;
|
||||
}
|
||||
|
||||
static void M_TextInputToWordBegin(textinput_t *input, boolean move_sel)
|
||||
{
|
||||
// Hack, always move back 1 character if possible so if we press ctrl-left at a word beginning
|
||||
// we move to previous word
|
||||
if (input->cursor) --input->cursor;
|
||||
|
||||
// Skip spaces
|
||||
while (input->cursor && isspace(input->buffer[input->cursor]))
|
||||
--input->cursor;
|
||||
|
||||
// Skip word
|
||||
while (input->cursor && !isspace(input->buffer[input->cursor]))
|
||||
--input->cursor;
|
||||
|
||||
// Unless we reached beginning of line, we're pointing at a space before word, so move cursor
|
||||
// forward to fix that
|
||||
if (input->cursor) ++input->cursor;
|
||||
|
||||
if (move_sel) input->select = input->cursor;
|
||||
}
|
||||
|
||||
void M_TextInputInit(textinput_t *input, char *buffer, size_t buffer_size)
|
||||
{
|
||||
input->buffer = buffer;
|
||||
input->buffer_size = buffer_size;
|
||||
|
||||
M_TextInputClear(input);
|
||||
}
|
||||
|
||||
void M_TextInputClear(textinput_t *input)
|
||||
{
|
||||
input->cursor = 0;
|
||||
input->select = 0;
|
||||
input->length = 0;
|
||||
}
|
||||
|
||||
void M_TextInputSetString(textinput_t *input, const char *c)
|
||||
{
|
||||
memset(input->buffer, 0, input->buffer_size);
|
||||
strcpy(input->buffer, c);
|
||||
input->cursor = input->select = input->length = strlen(c);
|
||||
}
|
||||
|
||||
boolean M_TextInputHandle(textinput_t *input, INT32 key)
|
||||
{
|
||||
if (key == KEY_LSHIFT || key == KEY_RSHIFT
|
||||
|| key == KEY_LCTRL || key == KEY_RCTRL
|
||||
|| key == KEY_LALT || key == KEY_RALT)
|
||||
return false;
|
||||
|
||||
//if ((cv_keyboardlayout.value != 3 && ctrldown) || (cv_keyboardlayout.value == 3 && ctrldown && !altdown))
|
||||
if (ctrldown)
|
||||
{
|
||||
if (key == 'x' || key == 'X')
|
||||
{
|
||||
if (input->select > input->cursor)
|
||||
I_ClipboardCopy(&input->buffer[input->cursor], input->select-input->cursor);
|
||||
else
|
||||
I_ClipboardCopy(&input->buffer[input->select], input->cursor-input->select);
|
||||
M_TextInputDelSelection(input);
|
||||
return true;
|
||||
}
|
||||
else if (key == 'c' || key == 'C')
|
||||
{
|
||||
if (input->select > input->cursor)
|
||||
I_ClipboardCopy(&input->buffer[input->cursor], input->select-input->cursor);
|
||||
else
|
||||
I_ClipboardCopy(&input->buffer[input->select], input->cursor-input->select);
|
||||
return true;
|
||||
}
|
||||
else if (key == 'v' || key == 'V')
|
||||
{
|
||||
const char *paste = I_ClipboardPaste();
|
||||
if (input->select != input->cursor)
|
||||
M_TextInputDelSelection(input);
|
||||
if (paste != NULL)
|
||||
M_TextInputAddString(input, paste);
|
||||
return true;
|
||||
}
|
||||
else if (key == 'w' || key == 'W')
|
||||
{
|
||||
size_t word_start, word_end, i;
|
||||
word_end = i = input->cursor;
|
||||
|
||||
// Unless we're pointing at the beginning of line, decrement i so we only start
|
||||
// removing symbols that come before the cursor
|
||||
if (i) --i;
|
||||
|
||||
// We might be pointing to spaces, skip them first
|
||||
while (i && isspace(input->buffer[i]))
|
||||
--i;
|
||||
|
||||
// Now skip the "word"
|
||||
while (i && !isspace(input->buffer[i]))
|
||||
--i;
|
||||
|
||||
// Unless we reached beginning of line, i is pointing at first space that was found
|
||||
// before word start, and we don't want to remove it
|
||||
if (i) ++i;
|
||||
|
||||
word_start = i;
|
||||
|
||||
if (word_start != word_end)
|
||||
M_TextInputDel(input, word_start, word_end);
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (key == KEY_RIGHTARROW)
|
||||
M_TextInputToWordEnd(input, !shiftdown);
|
||||
else if (key == KEY_LEFTARROW)
|
||||
M_TextInputToWordBegin(input, !shiftdown);
|
||||
|
||||
// Select all
|
||||
if (key == 'a' || key == 'A')
|
||||
{
|
||||
input->select = 0;
|
||||
input->cursor = input->length;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ...why shouldn't it eat the key? if it doesn't, it just means you
|
||||
// can control Sonic from the console, which is silly
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key == KEY_LEFTARROW)
|
||||
{
|
||||
if (input->cursor != 0)
|
||||
--input->cursor;
|
||||
if (!shiftdown)
|
||||
input->select = input->cursor;
|
||||
return true;
|
||||
}
|
||||
else if (key == KEY_RIGHTARROW)
|
||||
{
|
||||
if (input->cursor < input->length)
|
||||
++input->cursor;
|
||||
if (!shiftdown)
|
||||
input->select = input->cursor;
|
||||
return true;
|
||||
}
|
||||
else if (key == KEY_HOME)
|
||||
{
|
||||
input->cursor = 0;
|
||||
if (!shiftdown)
|
||||
input->select = input->cursor;
|
||||
return true;
|
||||
}
|
||||
else if (key == KEY_END)
|
||||
{
|
||||
input->cursor = input->length;
|
||||
if (!shiftdown)
|
||||
input->select = input->cursor;
|
||||
return true;
|
||||
}
|
||||
|
||||
// backspace and delete command prompt
|
||||
if (input->select != input->cursor)
|
||||
{
|
||||
if (key == KEY_BACKSPACE || key == KEY_DEL)
|
||||
{
|
||||
M_TextInputDelSelection(input);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (key == KEY_BACKSPACE)
|
||||
{
|
||||
M_TextInputDelChar(input);
|
||||
return true;
|
||||
}
|
||||
else if (key == KEY_DEL)
|
||||
{
|
||||
if (input->cursor == input->length)
|
||||
return true;
|
||||
++input->cursor;
|
||||
M_TextInputDelChar(input);
|
||||
return true;
|
||||
}
|
||||
|
||||
// allow people to use keypad in console (good for typing IP addresses) - Calum
|
||||
if (key >= KEY_KEYPAD7 && key <= KEY_KPADDEL)
|
||||
{
|
||||
char keypad_translation[] = {'7','8','9','-',
|
||||
'4','5','6','+',
|
||||
'1','2','3',
|
||||
'0','.'};
|
||||
|
||||
key = keypad_translation[key - KEY_KEYPAD7];
|
||||
}
|
||||
else if (key == KEY_KPADSLASH)
|
||||
key = '/';
|
||||
|
||||
// same capslock code as hu_stuff.c's HU_responder. Check there for details.
|
||||
key = CON_ShiftChar(key);
|
||||
|
||||
// enter a char into the command prompt
|
||||
if (key < 32 || key > 127)
|
||||
return false;
|
||||
|
||||
// add key to cmd line here
|
||||
if (key >= 'A' && key <= 'Z' && !(shiftdown ^ capslock)) //this is only really necessary for dedicated servers
|
||||
key = key + 'a' - 'A';
|
||||
|
||||
if (input->select != input->cursor)
|
||||
M_TextInputDelSelection(input);
|
||||
M_TextInputAddChar(input, key);
|
||||
|
||||
return true;
|
||||
}
|
||||
23
src/m_textinput.h
Normal file
23
src/m_textinput.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef __M_TEXTINPUT__
|
||||
#define __M_TEXTINPUT__
|
||||
|
||||
#include "doomtype.h"
|
||||
|
||||
typedef struct textinput_s {
|
||||
size_t cursor;
|
||||
size_t select;
|
||||
size_t length;
|
||||
|
||||
char *buffer;
|
||||
size_t buffer_size;
|
||||
} textinput_t;
|
||||
|
||||
void M_TextInputInit(textinput_t *input, char *buffer, size_t buffer_size);
|
||||
|
||||
void M_TextInputClear(textinput_t *input);
|
||||
|
||||
void M_TextInputSetString(textinput_t *input, const char *c);
|
||||
|
||||
boolean M_TextInputHandle(textinput_t *input, INT32 key);
|
||||
|
||||
#endif
|
||||
|
|
@ -815,27 +815,83 @@ static void ST_overlayDrawer(void)
|
|||
}
|
||||
}
|
||||
|
||||
void ST_DrawDemoTitleEntry(void)
|
||||
static void ST_DrawTextInput(INT32 x, INT32 y, textinput_t *input, INT32 flags)
|
||||
{
|
||||
static UINT8 skullAnimCounter = 0;
|
||||
char *nametodraw;
|
||||
static const INT32 MAXINPUTWIDTH = (MAXSTRINGLENGTH-1)*8;
|
||||
|
||||
if (renderisnewtic)
|
||||
skullAnimCounter++;
|
||||
|
||||
skullAnimCounter %= 8;
|
||||
|
||||
nametodraw = demo.titlename;
|
||||
while (V_StringWidth(nametodraw, 0) > MAXSTRINGLENGTH*8 - 8)
|
||||
nametodraw++;
|
||||
char nametodraw[MAXSTRINGLENGTH*2+1] = {0};
|
||||
|
||||
#define x (BASEVIDWIDTH/2 - 139)
|
||||
#define y (BASEVIDHEIGHT/2)
|
||||
size_t drawstart = 0;
|
||||
size_t drawend = 0; // Only used for selection
|
||||
|
||||
INT32 skullx = x;
|
||||
|
||||
while (V_SubStringWidth(input->buffer+drawstart, input->cursor-drawstart, V_ALLOWLOWERCASE) > MAXINPUTWIDTH)
|
||||
++drawstart;
|
||||
|
||||
size_t drawlength = V_SubStringLengthToFit(input->buffer+drawstart, MAXINPUTWIDTH+8, V_ALLOWLOWERCASE)+1;
|
||||
drawend = drawstart + drawlength;
|
||||
|
||||
memcpy(nametodraw, input->buffer+drawstart, drawlength);
|
||||
|
||||
|
||||
if (input->length)
|
||||
skullx += V_SubStringWidth(nametodraw, input->cursor-drawstart, V_ALLOWLOWERCASE);
|
||||
|
||||
V_DrawString(x, y, V_ALLOWLOWERCASE|flags, nametodraw);
|
||||
|
||||
// draw text cursor for name
|
||||
if (skullAnimCounter < 4) // blink cursor
|
||||
V_DrawCharacter(skullx, y+3, '_'|flags, false);
|
||||
|
||||
// draw selection
|
||||
if (input->select != input->cursor)
|
||||
{
|
||||
size_t start = min(input->select, input->cursor);
|
||||
size_t end = max(input->select, input->cursor);
|
||||
|
||||
INT32 startx = 0;
|
||||
INT32 width = 0;
|
||||
|
||||
// I couldn't figure out one formula so here's bunch of separate cases
|
||||
if (start < drawstart && end > drawend) // Selection covers whole visible portion of demo name
|
||||
{
|
||||
startx = -2;
|
||||
width = V_StringWidth(nametodraw, V_ALLOWLOWERCASE);
|
||||
}
|
||||
else if (start < drawstart) // Only left side of selection is off visible part
|
||||
{
|
||||
startx = -2;
|
||||
size_t len = (end - start) - (drawstart - start);
|
||||
width = V_SubStringWidth(nametodraw, len, V_ALLOWLOWERCASE)+2;
|
||||
}
|
||||
else if (end > drawend) // Only right side of selection is off visible part
|
||||
{
|
||||
startx = V_SubStringWidth(nametodraw, start-drawstart, V_ALLOWLOWERCASE);
|
||||
width = V_StringWidth(nametodraw+(start-drawstart), V_ALLOWLOWERCASE)+2;
|
||||
}
|
||||
else // All selection is on visible part
|
||||
{
|
||||
startx = V_SubStringWidth(nametodraw, start-drawstart, V_ALLOWLOWERCASE);
|
||||
width = V_SubStringWidth(nametodraw+(start-drawstart), end-start, V_ALLOWLOWERCASE);
|
||||
}
|
||||
|
||||
V_DrawFill(x+startx, y, width, 8, 103|V_TRANSLUCENT|flags);
|
||||
}
|
||||
}
|
||||
|
||||
void ST_DrawDemoTitleEntry(void)
|
||||
{
|
||||
#define x (BASEVIDWIDTH/2 - 139)
|
||||
#define y (BASEVIDHEIGHT/2)
|
||||
M_DrawTextBox(x, y + 4, MAXSTRINGLENGTH, 1);
|
||||
V_DrawString(x + 8, y + 12, V_ALLOWLOWERCASE, nametodraw);
|
||||
if (skullAnimCounter < 4)
|
||||
V_DrawCharacter(x + 8 + V_StringWidth(nametodraw, 0), y + 12,
|
||||
'_' | 0x80, false);
|
||||
|
||||
ST_DrawTextInput(x + 8, y + 12, &demo.titlenameinput, 0);
|
||||
|
||||
M_DrawTextBox(x + 30, y - 24, 26, 1);
|
||||
V_DrawString(x + 38, y - 16, V_ALLOWLOWERCASE, "Enter the name of the replay.");
|
||||
|
|
@ -843,8 +899,8 @@ void ST_DrawDemoTitleEntry(void)
|
|||
M_DrawTextBox(x + 50, y + 20, 20, 1);
|
||||
V_DrawThinString(x + 58, y + 28, V_ALLOWLOWERCASE, "Escape - Cancel");
|
||||
V_DrawRightAlignedThinString(x + 220, y + 28, V_ALLOWLOWERCASE, "Enter - Confirm");
|
||||
#undef x
|
||||
#undef y
|
||||
#undef x
|
||||
#undef y
|
||||
}
|
||||
|
||||
// MayonakaStatic: draw Midnight Channel's TV-like borders
|
||||
|
|
|
|||
|
|
@ -2952,11 +2952,14 @@ INT32 V_LevelNameHeight(const char *string)
|
|||
//
|
||||
// Find string width from hu_font chars
|
||||
//
|
||||
INT32 V_StringWidth(const char *string, INT32 option)
|
||||
INT32 V_SubStringWidth(const char *string, INT32 length, INT32 option)
|
||||
{
|
||||
INT32 c, w = 0;
|
||||
INT32 spacewidth = 4, charwidth = 0;
|
||||
size_t i;
|
||||
ssize_t i;
|
||||
|
||||
if (length < 0)
|
||||
length = strlen(string);
|
||||
|
||||
switch (option & V_SPACINGMASK)
|
||||
{
|
||||
|
|
@ -2972,7 +2975,7 @@ INT32 V_StringWidth(const char *string, INT32 option)
|
|||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < strlen(string); i++)
|
||||
for (i = 0; string[i] && i < length; i++)
|
||||
{
|
||||
c = string[i];
|
||||
if ((UINT8)c & 0x80) //color parsing! -Inuyasha 2.16.09
|
||||
|
|
@ -2994,11 +2997,14 @@ INT32 V_StringWidth(const char *string, INT32 option)
|
|||
//
|
||||
// Find string width from hu_font chars, 0.5x scale
|
||||
//
|
||||
INT32 V_SmallStringWidth(const char *string, INT32 option)
|
||||
INT32 V_SmallSubStringWidth(const char *string, INT32 length, INT32 option)
|
||||
{
|
||||
INT32 c, w = 0;
|
||||
INT32 spacewidth = 2, charwidth = 0;
|
||||
size_t i;
|
||||
ssize_t i;
|
||||
|
||||
if (length < 0)
|
||||
length = strlen(string);
|
||||
|
||||
switch (option & V_SPACINGMASK)
|
||||
{
|
||||
|
|
@ -3014,7 +3020,7 @@ INT32 V_SmallStringWidth(const char *string, INT32 option)
|
|||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < strlen(string); i++)
|
||||
for (i = 0; string[i] && i < length; i++)
|
||||
{
|
||||
c = string[i];
|
||||
if ((UINT8)c & 0x80) //color parsing! -Inuyasha 2.16.09
|
||||
|
|
@ -3033,12 +3039,15 @@ INT32 V_SmallStringWidth(const char *string, INT32 option)
|
|||
//
|
||||
// Find string width from tny_font chars
|
||||
//
|
||||
INT32 V_ThinStringWidth(const char *string, INT32 option)
|
||||
INT32 V_ThinSubStringWidth(const char *string, INT32 length, INT32 option)
|
||||
{
|
||||
INT32 c, w = 0;
|
||||
INT32 spacewidth = 2, charwidth = 0;
|
||||
boolean lowercase = (option & V_ALLOWLOWERCASE);
|
||||
size_t i;
|
||||
ssize_t i;
|
||||
|
||||
if (length < 0)
|
||||
length = strlen(string);
|
||||
|
||||
switch (option & V_SPACINGMASK)
|
||||
{
|
||||
|
|
@ -3055,7 +3064,7 @@ INT32 V_ThinStringWidth(const char *string, INT32 option)
|
|||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < strlen(string); i++)
|
||||
for (i = 0; string[i] && i < length; i++)
|
||||
{
|
||||
c = string[i];
|
||||
if ((UINT8)c & 0x80) //color parsing! -Inuyasha 2.16.09
|
||||
|
|
@ -3070,7 +3079,7 @@ INT32 V_ThinStringWidth(const char *string, INT32 option)
|
|||
else
|
||||
{
|
||||
w += (charwidth ? charwidth
|
||||
: ((option & V_6WIDTHSPACE && i < strlen(string)-1) ? max(1, fontv[TINY_FONT].font[c]->width-1) // Reuse this flag for the alternate bunched-up spacing
|
||||
: ((option & V_6WIDTHSPACE && i < (ssize_t)strlen(string)-1) ? max(1, fontv[TINY_FONT].font[c]->width-1) // Reuse this flag for the alternate bunched-up spacing
|
||||
: fontv[TINY_FONT].font[c]->width));
|
||||
}
|
||||
}
|
||||
|
|
@ -3082,10 +3091,57 @@ INT32 V_ThinStringWidth(const char *string, INT32 option)
|
|||
//
|
||||
// Find string width from tny_font chars, 0.5x scale
|
||||
//
|
||||
INT32 V_SmallThinStringWidth(const char *string, INT32 option)
|
||||
/*INT32 V_SmallThinStringWidth(const char *string, INT32 option)
|
||||
{
|
||||
INT32 w = V_ThinStringWidth(string, option)<<FRACBITS;
|
||||
return w/2 + FRACUNIT; // +FRACUNIT because otherwise it's offset wrong.
|
||||
}*/
|
||||
|
||||
//
|
||||
|
||||
// Find maximum length for substring taken from current string to fit into given width
|
||||
|
||||
//
|
||||
|
||||
INT32 V_SubStringLengthToFit(const char *string, INT32 width, INT32 option)
|
||||
{
|
||||
INT32 c, w = 0;
|
||||
INT32 spacewidth = 4, charwidth = 0;
|
||||
INT32 i;
|
||||
|
||||
switch (option & V_SPACINGMASK)
|
||||
{
|
||||
case V_MONOSPACE:
|
||||
spacewidth = 8;
|
||||
/* FALLTHRU */
|
||||
case V_OLDSPACING:
|
||||
charwidth = 8;
|
||||
break;
|
||||
|
||||
case V_6WIDTHSPACE:
|
||||
spacewidth = 6;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; string[i] && w < width; i++)
|
||||
{
|
||||
c = string[i];
|
||||
|
||||
if ((UINT8)c >= 0x80 && (UINT8)c <= 0x8F) //color parsing! -Inuyasha 2.16.09
|
||||
continue;
|
||||
|
||||
c = toupper(c) - HU_FONTSTART;
|
||||
|
||||
if (c < 0 || c >= HU_FONTSIZE || !fontv[HU_FONT].font[c])
|
||||
w += spacewidth;
|
||||
else
|
||||
w += (charwidth ? charwidth : fontv[HU_FONT].font[c]->width);
|
||||
}
|
||||
|
||||
return max(i-1, 0);
|
||||
}
|
||||
|
||||
boolean *heatshifter = NULL;
|
||||
|
|
|
|||
|
|
@ -329,13 +329,16 @@ INT16 V_LevelActNumWidth(UINT8 num); // act number width
|
|||
INT32 V_CreditStringWidth(const char *string);
|
||||
|
||||
// Find string width from hu_font chars
|
||||
INT32 V_StringWidth(const char *string, INT32 option);
|
||||
INT32 V_SubStringWidth(const char *string, INT32 length, INT32 option);
|
||||
#define V_StringWidth(string, option) V_SubStringWidth(string, -1, option)
|
||||
// Find string width from hu_font chars, 0.5x scale
|
||||
INT32 V_SmallStringWidth(const char *string, INT32 option);
|
||||
INT32 V_SmallSubStringWidth(const char *string, INT32 length, INT32 option);
|
||||
#define V_SmallStringWidth(string, option) V_SmallSubStringWidth(string, -1, option)
|
||||
// Find string width from tny_font chars
|
||||
INT32 V_ThinStringWidth(const char *string, INT32 option);
|
||||
// Find string width from tny_font chars, 0.5x scale
|
||||
INT32 V_SmallThinStringWidth(const char *string, INT32 option);
|
||||
INT32 V_ThinSubStringWidth(const char *string, INT32 length, INT32 option);
|
||||
#define V_ThinStringWidth(string, option) V_ThinSubStringWidth(string, -1, option)
|
||||
|
||||
INT32 V_SubStringLengthToFit(const char *string, INT32 width, INT32 option);
|
||||
|
||||
void V_DoPostProcessor(INT32 view, INT32 param);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue