From 5de315225c6ec6ccf337ffa1a01e6a961fdb112d Mon Sep 17 00:00:00 2001 From: NepDisk Date: Fri, 15 Aug 2025 12:24:17 -0400 Subject: [PATCH] Use textinput_t for replay save prompt too --- src/g_demo.c | 39 +++++++++-------------- src/g_demo.h | 2 ++ src/st_stuff.c | 84 +++++++++++++++++++++++++++++++++++++++++--------- src/v_video.c | 49 ++++++++++++++++++++++++++++- src/v_video.h | 2 ++ 5 files changed, 137 insertions(+), 39 deletions(-) diff --git a/src/g_demo.c b/src/g_demo.c index 1f34d1cc5..c8b740afe 100644 --- a/src/g_demo.c +++ b/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; } diff --git a/src/g_demo.h b/src/g_demo.h index 228822085..9271d2240 100644 --- a/src/g_demo.h +++ b/src/g_demo.h @@ -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 diff --git a/src/st_stuff.c b/src/st_stuff.c index e414fb5eb..fc96f3a03 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -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 diff --git a/src/v_video.c b/src/v_video.c index 7cc9f0ef7..3c7db5854 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -3079,7 +3079,7 @@ INT32 V_ThinSubStringWidth(const char *string, INT32 length, 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)); } } @@ -3097,6 +3097,53 @@ INT32 V_SmallThinStringWidth(const char *string, INT32 option) 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; INT32 lastheight = 0; INT32 heatindex[MAXSPLITSCREENPLAYERS] = {0, 0, 0, 0}; diff --git a/src/v_video.h b/src/v_video.h index 5b5da8d77..b404c1ff0 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -338,6 +338,8 @@ INT32 V_SmallSubStringWidth(const char *string, INT32 length, 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); void V_DrawPatchFill(patch_t *pat);