Improved handling of multi-line strings:

- Centered/right-aligned string drawing now properly handles newlines
- Measuring string width/height takes newlines into account
- String height also takes V_RETURN8 into account
- Cleaned up menu message code, removed now-redundant M_StringHeight
This commit is contained in:
spherallic 2023-03-09 11:20:26 +01:00
parent 759ac6cf35
commit 66042ef8a1
3 changed files with 87 additions and 182 deletions

View file

@ -4957,22 +4957,6 @@ static void M_DrawCenteredMenu(void)
}
}
//
// M_StringHeight
//
// Find string height from hu_font chars
//
static inline size_t M_StringHeight(const char *string)
{
size_t h = 8, i;
for (i = 0; i < strlen(string); i++)
if (string[i] == '\n')
h += 8;
return h;
}
// ==========================================================================
// Extraneous menu patching functions
// ==========================================================================
@ -6099,56 +6083,16 @@ menu_t MessageDef =
NULL
};
void M_StartMessage(const char *string, void *routine,
menumessagetype_t itemtype)
void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtype)
{
size_t max = 0, start = 0, i, strlines;
static char *message = NULL;
static char *message;
Z_Free(message);
message = Z_StrDup(string);
message = V_WordWrap(0,0,V_ALLOWLOWERCASE,string);
DEBFILE(message);
// Rudementary word wrapping.
// Simple and effective. Does not handle nonuniform letter sizes, colors, etc. but who cares.
strlines = 0;
for (i = 0; message[i]; i++)
{
if (message[i] == ' ')
{
start = i;
max += 4;
}
else if (message[i] == '\n')
{
strlines = i;
start = 0;
max = 0;
continue;
}
else
max += 8;
// Start trying to wrap if presumed length exceeds the screen width.
if (max >= BASEVIDWIDTH && start > 0)
{
message[start] = '\n';
max -= (start-strlines)*8;
strlines = start;
start = 0;
}
}
start = 0;
max = 0;
M_StartControlPanel(); // can't put menuactive to true
if (currentMenu == &MessageDef) // Prevent recursion
MessageDef.prevMenu = &MainDef;
else
MessageDef.prevMenu = currentMenu;
MessageDef.prevMenu = (currentMenu == &MessageDef) ? &MainDef : currentMenu; // Prevent recursion
MessageDef.menuitems[0].text = message;
MessageDef.menuitems[0].alphaKey = (UINT8)itemtype;
if (!routine && itemtype != MM_NOTHING) itemtype = MM_NOTHING;
@ -6167,51 +6111,17 @@ void M_StartMessage(const char *string, void *routine,
MessageDef.menuitems[0].itemaction = routine;
break;
}
//added : 06-02-98: now draw a textbox around the message
// compute lenght max and the numbers of lines
for (strlines = 0; *(message+start); strlines++)
{
for (i = 0;i < strlen(message+start);i++)
{
if (*(message+start+i) == '\n')
{
if (i > max)
max = i;
start += i;
i = (size_t)-1; //added : 07-02-98 : damned!
start++;
break;
}
}
MessageDef.x = (INT16)((BASEVIDWIDTH - V_StringWidth(message, 0)-32)/2);
MessageDef.y = (INT16)((BASEVIDHEIGHT - V_StringHeight(message, V_RETURN8))/2);
if (i == strlen(message+start))
start += i;
}
MessageDef.x = (INT16)((BASEVIDWIDTH - 8*max-16)/2);
MessageDef.y = (INT16)((BASEVIDHEIGHT - M_StringHeight(message))/2);
MessageDef.lastOn = (INT16)((strlines<<8)+max);
//M_SetupNextMenu();
currentMenu = &MessageDef;
itemOn = 0;
}
#define MAXMSGLINELEN 256
static void M_DrawMessageMenu(void)
{
INT32 y = currentMenu->y;
size_t i, start = 0;
INT16 max;
char string[MAXMSGLINELEN];
INT32 mlines;
const char *msg = currentMenu->menuitems[0].text;
mlines = currentMenu->lastOn>>8;
max = (INT16)((UINT8)(currentMenu->lastOn & 0xFF)*8);
// hack: draw RA background in RA menus
if (gamestate == GS_TIMEATTACK)
{
@ -6235,51 +6145,8 @@ static void M_DrawMessageMenu(void)
V_DrawFadeScreen(0xFF00, curfadevalue);
}
M_DrawTextBox(currentMenu->x, y - 8, (max+7)>>3, mlines);
while (*(msg+start))
{
size_t len = strlen(msg+start);
for (i = 0; i < len; i++)
{
if (*(msg+start+i) == '\n')
{
memset(string, 0, MAXMSGLINELEN);
if (i >= MAXMSGLINELEN)
{
CONS_Printf("M_DrawMessageMenu: too long segment in %s\n", msg);
return;
}
else
{
strncpy(string,msg+start, i);
string[i] = '\0';
start += i;
i = (size_t)-1; //added : 07-02-98 : damned!
start++;
}
break;
}
}
if (i == strlen(msg+start))
{
if (i >= MAXMSGLINELEN)
{
CONS_Printf("M_DrawMessageMenu: too long segment in %s\n", msg);
return;
}
else
{
strcpy(string, msg + start);
start += i;
}
}
V_DrawString((BASEVIDWIDTH - V_StringWidth(string, 0))/2,y,V_ALLOWLOWERCASE,string);
y += 8; //hu_font.chars[0]->height;
}
M_DrawTextBox(currentMenu->x, currentMenu->y - 8, 2+V_StringWidth(msg, 0)/8, V_StringHeight(msg, V_RETURN8)/8);
V_DrawCenteredString(BASEVIDWIDTH/2, currentMenu->y, V_ALLOWLOWERCASE|V_RETURN8, msg);
}
// default message handler

View file

@ -1990,10 +1990,7 @@ void V_DrawFontCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed, fi
flags = c & ~(V_CHARCOLORMASK | V_PARAMMASK);
c &= 0x7f;
if (lowercaseallowed)
c -= FONTSTART;
else
c = toupper(c) - FONTSTART;
c = (lowercaseallowed ? c : toupper(c)) - FONTSTART;
if (c < 0 || c >= FONTSIZE || !font.chars[c])
return;
@ -2009,8 +2006,7 @@ void V_DrawFontCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed, fi
char *V_FontWordWrap(INT32 x, INT32 w, INT32 option, fixed_t scale, const char *string, fontdef_t font)
{
int c;
size_t chw, i, lastusablespace = 0;
size_t slen;
size_t slen, chw, i, lastusablespace = 0;
char *newstring = Z_StrDup(string);
INT32 spacewidth = font.spacewidth, charwidth = 0;
@ -2048,10 +2044,7 @@ char *V_FontWordWrap(INT32 x, INT32 w, INT32 option, fixed_t scale, const char *
continue;
}
if (!(option & V_ALLOWLOWERCASE))
c = toupper(c);
c -= FONTSTART;
c = (option & V_ALLOWLOWERCASE ? c : toupper(c)) - FONTSTART;
if (c < 0 || c >= FONTSIZE || !font.chars[c])
{
chw = spacewidth;
@ -2148,20 +2141,11 @@ void V_DrawFontStringAtFixed(fixed_t x, fixed_t y, INT32 option, fixed_t pscale,
if (*ch == '\n')
{
cx = x;
if (option & V_RETURN8)
cy += FixedMul((8<<FRACBITS), dupy);
else
cy += FixedMul((font.linespacing<<FRACBITS), dupy);
cy += FixedMul(((option & V_RETURN8) ? 8 : font.linespacing)<<FRACBITS, dupy);
continue;
}
c = *ch;
if (!lowercase)
c = toupper(c);
c -= FONTSTART;
c = (lowercase ? *ch : toupper(*ch)) - FONTSTART;
if (c < 0 || c >= FONTSIZE || !font.chars[c])
{
cx += FixedMul((spacewidth<<FRACBITS), dupx);
@ -2190,16 +2174,58 @@ void V_DrawFontStringAtFixed(fixed_t x, fixed_t y, INT32 option, fixed_t pscale,
}
}
#define MAXLINELEN 256
void V_DrawAlignedFontStringAtFixed(fixed_t x, fixed_t y, INT32 option, fixed_t pscale, fixed_t vscale, const char *string, fontdef_t font, boolean center)
{
char line[MAXLINELEN];
size_t i, start = 0;
fixed_t lx = x, ly = y;
while (*(string+start))
{
for (i = 0; i < strlen(string+start); i++)
{
if (*(string+start+i) == '\n')
{
memset(line, 0, MAXLINELEN);
if (i >= MAXLINELEN)
{
CONS_Printf("V_DrawAlignedFontStringAtFixed: a line exceeds max. length %d (string: %s)\n", MAXLINELEN, string);
return;
}
strncpy(line,string + start, i);
line[i] = '\0';
start += i + 1;
i = (size_t)-1; //added : 07-02-98 : damned!
break;
}
}
if (i == strlen(string + start))
{
if (i >= MAXLINELEN)
{
CONS_Printf("V_DrawAlignedFontStringAtFixed: a line exceeds max. length %d (string: %s)\n", MAXLINELEN, string);
return;
}
strcpy(line, string + start);
start += i;
}
lx = x - (V_FontStringWidth(line, option, font)*pscale) / (center ? 2 : 1);
V_DrawFontStringAtFixed(lx, ly, option, pscale, vscale, line, font);
ly += FixedMul(((option & V_RETURN8) ? 8 : font.linespacing)<<FRACBITS, vscale);
}
}
void V_DrawCenteredFontStringAtFixed(fixed_t x, fixed_t y, INT32 option, fixed_t pscale, fixed_t vscale, const char *string, fontdef_t font)
{
x -= (V_FontStringWidth(string, option, font)*pscale)/2;
V_DrawFontStringAtFixed(x, y, option, pscale, vscale, string, font);
V_DrawAlignedFontStringAtFixed(x, y, option, pscale, vscale, string, font, true);
}
void V_DrawRightAlignedFontStringAtFixed(fixed_t x, fixed_t y, INT32 option, fixed_t pscale, fixed_t vscale, const char *string, fontdef_t font)
{
x -= V_FontStringWidth(string, option, font)*pscale;
V_DrawFontStringAtFixed(x, y, option, pscale, vscale, string, font);
V_DrawAlignedFontStringAtFixed(x, y, option, pscale, vscale, string, font, false);
}
// Draws a tallnum. Replaces two functions in y_inter and st_stuff
@ -2324,10 +2350,7 @@ static void V_DrawNameTagLine(INT32 x, INT32 y, INT32 option, fixed_t scale, UIN
continue;
}
c = toupper(*ch);
c -= FONTSTART;
// character does not exist or is a space
c = toupper(*ch) - FONTSTART;
if (c < 0 || c >= FONTSIZE || !ntb_font.chars[c] || !nto_font.chars[c])
{
cx += FixedMul((ntb_font.spacewidth * dupx)*FRACUNIT, scale);
@ -2464,9 +2487,8 @@ INT32 V_CountNameTagLines(const char *string)
//
INT32 V_FontStringWidth(const char *string, INT32 option, fontdef_t font)
{
INT32 c, w = 0;
INT32 c, w = 0, wline = 0;
INT32 spacewidth = font.spacewidth, charwidth = 0;
size_t i;
switch (option & V_SPACINGMASK)
{
@ -2482,16 +2504,24 @@ INT32 V_FontStringWidth(const char *string, INT32 option, fontdef_t font)
break;
}
for (i = 0; i < strlen(string); i++)
for (size_t i = 0; i < strlen(string); i++)
{
if (string[i] == '\n')
{
if (wline < w) wline = w;
w = 0;
continue;
}
if (string[i] & 0x80)
continue;
c = ((option & V_ALLOWLOWERCASE ? string[i] : toupper(string[i])) - FONTSTART);
c = (option & V_ALLOWLOWERCASE ? string[i] : toupper(string[i])) - FONTSTART;
if (c < 0 || c >= FONTSIZE || !font.chars[c])
w += spacewidth;
else
w += (charwidth ? charwidth : (font.chars[c]->width)) + font.kerning;
}
w = max(wline, w);
if (option & (V_NOSCALESTART|V_NOSCALEPATCH))
w *= vid.dupx;
@ -2501,22 +2531,28 @@ INT32 V_FontStringWidth(const char *string, INT32 option, fontdef_t font)
// Find max string height from supplied font characters
//
INT32 V_FontStringHeight(const char *string, fontdef_t font)
INT32 V_FontStringHeight(const char *string, INT32 option, fontdef_t font)
{
INT32 c, h = 0;
size_t i;
INT32 c, h = 0, result = 0;
for (i = 0; i < strlen(string); i++)
for (size_t i = 0; i < strlen(string); i++)
{
c = string[i] - FONTSTART;
c = (option & V_ALLOWLOWERCASE ? string[i] : toupper(string[i])) - FONTSTART;
if (c < 0 || c >= FONTSIZE || !font.chars[c])
{
if (string[i] == '\n')
{
result += (option & V_RETURN8) ? 8 : font.linespacing;
h = 0;
}
continue;
}
if (font.chars[c]->height > h)
h = font.chars[c]->height;
}
return h;
return result + h;
}
boolean *heatshifter = NULL;

View file

@ -213,6 +213,7 @@ void V_DrawCenteredFontString(INT32 x, INT32 y, INT32 option, fixed_t pscale, fi
void V_DrawRightAlignedFontString(INT32 x, INT32 y, INT32 option, fixed_t pscale, fixed_t vscale, const char *string, fontdef_t font);
// Draw a string, using a supplied font and scale, at fixed_t coordinates.
void V_DrawFontStringAtFixed(fixed_t x, fixed_t y, INT32 option, fixed_t pscale, fixed_t vscale, const char *string, fontdef_t font);
void V_DrawAlignedFontStringAtFixed(fixed_t x, fixed_t y, INT32 option, fixed_t pscale, fixed_t vscale, const char *string, fontdef_t font, boolean center);
void V_DrawCenteredFontStringAtFixed(fixed_t x, fixed_t y, INT32 option, fixed_t pscale, fixed_t vscale, const char *string, fontdef_t font);
void V_DrawRightAlignedFontStringAtFixed(fixed_t x, fixed_t y, INT32 option, fixed_t pscale, fixed_t vscale, const char *string, fontdef_t font);
@ -266,7 +267,7 @@ INT32 V_CountNameTagLines(const char *string);
// Find string width or height from supplied font chars
INT32 V_FontStringWidth(const char *string, INT32 option, fontdef_t font);
INT32 V_FontStringHeight(const char *string, fontdef_t font);
INT32 V_FontStringHeight(const char *string, INT32 option, fontdef_t font);
// Defines for old string width functions.
#define V_StringWidth(str,o) V_FontStringWidth(str,o,hu_font)
@ -276,7 +277,8 @@ INT32 V_FontStringHeight(const char *string, fontdef_t font);
#define V_CreditStringWidth(str) V_FontStringWidth(str,0,cred_font)
#define V_NameTagWidth(str) V_FontStringWidth(str,0,ntb_font)
#define V_LevelNameWidth(str) V_FontStringWidth(str,V_ALLOWLOWERCASE,lt_font)
#define V_LevelNameHeight(str) V_FontStringHeight(str,lt_font)
#define V_LevelNameHeight(str) V_FontStringHeight(str,0,lt_font)
#define V_StringHeight(str,o) V_FontStringHeight(str,o,hu_font)
void V_DoPostProcessor(INT32 view, postimg_t type, INT32 param);