- add an option to the ListMenu class to draw to a fixed virtual screen size.

This allows proper dimensions in the stock menus and should make menu layout for mods a lot easier because coordinates are more predictable than with the clean factors.
The feature is opt-in for custom content. As long as only stock elements are used, the menu will be able to switch between both on its own, but as soon as custom classes or custom elements are used, the virtual size must be declared explicitly, defaulting to clean scaling.
This commit is contained in:
Christoph Oelckers 2020-10-04 10:15:52 +02:00
parent acd71f7019
commit f1b0f32786
8 changed files with 165 additions and 67 deletions

View File

@ -410,6 +410,29 @@ void CalcFullscreenScale(DrawParms *parms, double srcwidth, double srcheight, in
}
}
DEFINE_ACTION_FUNCTION(_Screen, GetFullscreenRect)
{
PARAM_PROLOGUE;
PARAM_FLOAT(virtw);
PARAM_FLOAT(virth);
PARAM_INT(fsmode);
if (!twod->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function");
DrawParms parms;
DoubleRect rect;
parms.viewport.width = twod->GetWidth();
parms.viewport.height = twod->GetHeight();
CalcFullscreenScale(&parms, virtw, virth, fsmode, rect);
if (numret >= 1) ret[0].SetFloat(rect.left);
if (numret >= 2) ret[1].SetFloat(rect.top);
if (numret >= 3) ret[2].SetFloat(rect.width);
if (numret >= 4) ret[3].SetFloat(rect.height);
return MIN(numret, 4);
}
//==========================================================================
//
// Draw parameter parsing
@ -460,6 +483,13 @@ bool SetTextureParms(F2DDrawer * drawer, DrawParms *parms, FGameTexture *img, do
parms->destheight = parms->texheight * CleanYfac;
break;
case DTA_CleanTop:
parms->x = (parms->x - 160.0) * CleanXfac + (GetWidth() * 0.5);
parms->y = (parms->y) * CleanYfac;
parms->destwidth = parms->texwidth * CleanXfac;
parms->destheight = parms->texheight * CleanYfac;
break;
case DTA_CleanNoMove:
parms->destwidth = parms->texwidth * CleanXfac;
parms->destheight = parms->texheight * CleanYfac;
@ -716,6 +746,7 @@ bool ParseDrawTextureTags(F2DDrawer *drawer, FGameTexture *img, double x, double
break;
case DTA_Clean:
case DTA_CleanTop:
boolval = ListGetInt(tags);
if (boolval)
{

View File

@ -130,6 +130,7 @@ enum
DTA_Rotate,
DTA_FlipOffsets, // Flips offsets when using DTA_FlipX and DTA_FlipY, this cannot be automatic due to unexpected behavior with unoffsetted graphics.
DTA_Indexed, // Use an indexed texture combined with the given translation.
DTA_CleanTop, // Like DTA_Clean but aligns to the top of the screen instead of the center.
};

View File

@ -270,6 +270,8 @@ static bool CheckSkipOptionBlock(FScanner &sc)
static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc)
{
bool sizeset = false;
bool sizecompatible = true;
sc.MustGetStringName("{");
while (!sc.CheckString("}"))
{
@ -303,6 +305,7 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc)
sc.ScriptError("Unknown menu class '%s'", sc.String);
}
desc->mClass = cls;
sizecompatible = false;
}
else if (sc.Compare("Selector"))
{
@ -382,14 +385,23 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc)
{
desc->mVirtWidth = -1;
}
if (sc.Compare("optclean"))
else if (sc.Compare("optclean"))
{
desc->mVirtWidth = -2;
}
else
{
sc.ScriptError("Invalid value '%s' for 'size'", sc.String);
}
}
}
else
{
// all item classes from which we know that they support sized scaling.
// If anything else comes through here the option to swich scaling mode is disabled for this menu.
static const char* const compatibles[] = { "StaticPatch", "StaticText", "StaticTextCentered", "TextItem", "PatchItem", "PlayerDisplay", nullptr };
if (sc.MatchString(compatibles) < 0) sizecompatible = false;
bool success = false;
FStringf buildname("ListMenuItem%s", sc.String);
PClass *cls = PClass::FindClass(buildname);
@ -521,6 +533,10 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc)
}
}
}
if (!sizeset && sizecompatible) // allow unclean scaling on this menu
{
desc->mVirtWidth = -2;
}
for (auto &p : desc->mItems)
{
GC::WriteBarrier(p);

View File

@ -231,7 +231,6 @@ ListMenu "EpisodeMenu"
ListMenu "SkillMenu"
{
IfGame(Doom, Chex)
{
StaticPatch 96, 14, "M_NEWG", 0, "$MNU_NEWGAME"
@ -289,6 +288,7 @@ ListMenu "LoadGameMenu"
StaticTextCentered 160, -10, "$MNU_LOADGAME"
Position 80,54
Class "LoadMenu" // uses its own implementation
Size Clean
}
//-------------------------------------------------------------------------------------------
@ -302,6 +302,7 @@ ListMenu "SaveGameMenu"
StaticTextCentered 160, -10, "$MNU_SAVEGAME"
Position 80,54
Class "SaveMenu" // uses its own implementation
Size Clean
}
//-------------------------------------------------------------------------------------------

View File

@ -124,6 +124,30 @@ struct TexMan
native static void SetCameraToTexture(Actor viewpoint, String texture, double fov);
}
enum EScaleMode
{
FSMode_None = 0,
FSMode_ScaleToFit = 1,
FSMode_ScaleToFill = 2,
FSMode_ScaleToFit43 = 3,
FSMode_ScaleToScreen = 4,
FSMode_ScaleToFit43Top = 5,
FSMode_ScaleToFit43Bottom = 6,
FSMode_ScaleToHeight = 7,
FSMode_Max,
// These all use ScaleToFit43, their purpose is to cut down on verbosity because they imply the virtual screen size.
FSMode_Predefined = 1000,
FSMode_Fit320x200 = 1000,
FSMode_Fit320x240,
FSMode_Fit640x400,
FSMode_Fit640x480,
FSMode_Fit320x200Top,
FSMode_Predefined_Max,
};
enum DrawTextureTags
{
TAG_USER = (1<<30),
@ -208,6 +232,7 @@ enum DrawTextureTags
DTA_Rotate,
DTA_FlipOffsets, // Flips offsets when using DTA_FlipX and DTA_FlipY, this cannot be automatic due to unexpected behavior with unoffsetted graphics.
DTA_Indexed, // Use an indexed texture combined with the given translation.
DTA_CleanTop, // Like DTA_Clean but aligns to the top of the screen instead of the center.
};
@ -257,6 +282,8 @@ struct Screen native
native static void ClearClipRect();
native static int, int, int, int GetClipRect();
native static int, int, int, int GetViewWindow();
native static double, double, double, double GetFullscreenRect(double vwidth, double vheight, int fsmode);
// This is a leftover of the abandoned Inventory.DrawPowerup method.

View File

@ -2,7 +2,7 @@
class ListMenuDescriptor : MenuDescriptor native
{
enum
enum EScale
{
CleanScale = -1,
OptCleanScale = -2
@ -249,10 +249,10 @@ class ListMenu : Menu
{
for(int i=0;i<mDesc.mItems.Size(); i++)
{
if (mDesc.mItems[i].mEnabled) mDesc.mItems[i].Drawer(mDesc.mSelectedItem == i);
if (mDesc.mItems[i].mEnabled) mDesc.mItems[i].Draw(mDesc.mSelectedItem == i, mDesc);
}
if (mDesc.mSelectedItem >= 0 && mDesc.mSelectedItem < mDesc.mItems.Size())
mDesc.mItems[mDesc.mSelectedItem].DrawSelector(mDesc.mSelectOfsX, mDesc.mSelectOfsY, mDesc.mSelector);
mDesc.mItems[mDesc.mSelectedItem].DrawSelector(mDesc.mSelectOfsX, mDesc.mSelectOfsY, mDesc.mSelector, mDesc);
Super.Drawer();
}

View File

@ -35,26 +35,54 @@
class ListMenuItem : MenuItemBase
{
virtual void DrawSelector(double xofs, double yofs, TextureID tex)
protected void DrawText(ListMenuDescriptor desc, Font fnt, int color, double x, double y, String text, bool ontop = false)
{
int w = desc ? desc.DisplayWidth() : ListMenuDescriptor.CleanScale;
int h = desc ? desc.DisplayHeight() : -1;
if (w == ListMenuDescriptor.CleanScale)
{
screen.DrawText(fnt, color, x, y, text, ontop? DTA_CleanTop : DTA_Clean, true);
}
else
{
screen.DrawText(fnt, color, x, y, text, DTA_VirtualWidth, w, DTA_VirtualHeight, h, DTA_FullscreenScale, FSMode_ScaleToFit43);
}
}
protected void DrawTexture(ListMenuDescriptor desc, TextureID tex, double x, double y, bool ontop = false)
{
int w = desc ? desc.DisplayWidth() : ListMenuDescriptor.CleanScale;
int h = desc ? desc.DisplayHeight() : -1;
if (w == ListMenuDescriptor.CleanScale)
{
screen.DrawTexture(tex, true, x, y, ontop ? DTA_CleanTop : DTA_Clean, true);
}
else
{
screen.DrawTexture(tex, true, x, y, DTA_VirtualWidth, w, DTA_VirtualHeight, h, DTA_FullscreenScale, FSMode_ScaleToFit43);
}
}
virtual void DrawSelector(double xofs, double yofs, TextureID tex, ListMenuDescriptor desc = null)
{
if (tex.isNull())
{
if ((Menu.MenuTime() % 8) < 6)
{
screen.DrawText(ConFont, OptionMenuSettings.mFontColorSelection,
(mXpos + xofs - 160) * CleanXfac + screen.GetWidth() / 2,
(mYpos + yofs - 100) * CleanYfac + screen.GetHeight() / 2,
"\xd",
DTA_CellX, 8 * CleanXfac,
DTA_CellY, 8 * CleanYfac
);
DrawText(desc, ConFont, OptionMenuSettings.mFontColorSelection, mXpos + xofs, mYpos + yofs + 8, "\xd");
}
}
else
{
screen.DrawTexture (tex, true, mXpos + xofs, mYpos + yofs, DTA_Clean, true);
DrawTexture(desc, tex, mXpos + xofs, mYpos + yofs);
}
}
// We cannot extend Drawer here because it is inherited from the parent class.
virtual void Draw(bool selected, ListMenuDescriptor desc)
{
Drawer(selected); // fall back to the legacy version, if not overridden
}
}
//=============================================================================
@ -82,43 +110,26 @@ class ListMenuItemStaticPatch : ListMenuItem
}
override void Drawer(bool selected)
override void Draw(bool selected, ListMenuDescriptor desc)
{
if (!mTexture.Exists())
{
return;
}
let font = generic_ui? NewSmallFont : mFont;
double x = mXpos;
Vector2 vec = TexMan.GetScaledSize(mTexture);
if (mYpos >= 0)
if (mSubstitute == "" || TexMan.OkForLocalization(mTexture, mSubstitute))
{
if (mSubstitute == "" || TexMan.OkForLocalization(mTexture, mSubstitute))
{
if (mCentered) x -= vec.X / 2;
screen.DrawTexture (mTexture, true, x, mYpos, DTA_Clean, true);
}
else
{
if (mCentered) x -= font.StringWidth(mSubstitute)/2;
screen.DrawText(font, mColor, x, mYpos, mSubstitute, DTA_Clean, true);
}
if (mCentered) x -= vec.X / 2;
DrawTexture(desc, mTexture, x, abs(mYpos), mYpos < 0);
}
else
{
x = (mXpos - 160) * CleanXfac + (Screen.GetWidth()>>1);
if (mSubstitute == "" || TexMan.OkForLocalization(mTexture, mSubstitute))
{
if (mCentered) x -= (vec.X * CleanXfac)/2;
screen.DrawTexture (mTexture, true, x, -mYpos*CleanYfac, DTA_CleanNoMove, true);
}
else
{
if (mCentered) x -= (font.StringWidth(mSubstitute) * CleanXfac)/2;
screen.DrawText(font, mColor, x, mYpos, mSubstitute, DTA_CleanNoMove, true);
}
let font = generic_ui ? NewSmallFont : mFont;
if (mCentered) x -= font.StringWidth(mSubstitute) / 2;
DrawText(desc, font, mColor, x, abs(mYpos), mSubstitute, mYpos < 0);
}
}
}
@ -162,25 +173,17 @@ class ListMenuItemStaticText : ListMenuItem
mCentered = centered;
}
override void Drawer(bool selected)
override void Draw(bool selected, ListMenuDescriptor desc)
{
if (mText.Length() != 0)
{
let font = generic_ui? NewSmallFont : mFont;
String text = Stringtable.Localize(mText);
if (mYpos >= 0)
{
double x = mXpos;
if (mCentered) x -= font.StringWidth(text)/2;
screen.DrawText(font, mColor, x, mYpos, text, DTA_Clean, true);
}
else
{
double x = (mXpos - 160) * CleanXfac + (Screen.GetWidth() >> 1);
if (mCentered) x -= (font.StringWidth(text) * CleanXfac)/2;
screen.DrawText (font, mColor, x, -mYpos*CleanYfac, text, DTA_CleanNoMove, true);
}
double x = mXpos;
if (mCentered) x -= font.StringWidth(text) / 2;
DrawText(desc, font, mColor, x, abs(mYpos), text, mYpos < 0);
}
}
}
@ -288,10 +291,10 @@ class ListMenuItemTextItem : ListMenuItemSelectable
mHotkey = hotkey.GetNextCodePoint(0);
}
override void Drawer(bool selected)
override void Draw(bool selected, ListMenuDescriptor desc)
{
let font = generic_ui? NewSmallFont : mFont;
screen.DrawText(font, selected ? mColorSelected : mColor, mXpos, mYpos, mText, DTA_Clean, true);
let font = generic_ui ? NewSmallFont : mFont;
DrawText(desc, font, selected ? mColorSelected : mColor, mXpos, mYpos, mText);
}
override int GetWidth()
@ -325,9 +328,9 @@ class ListMenuItemPatchItem : ListMenuItemSelectable
mTexture = patch;
}
override void Drawer(bool selected)
override void Draw(bool selected, ListMenuDescriptor desc)
{
screen.DrawTexture (mTexture, true, mXpos, mYpos, DTA_Clean, true);
DrawTexture(desc, mTexture, mXpos, mYpos);
}
override int GetWidth()

View File

@ -228,7 +228,7 @@ class ListMenuItemPlayerDisplay : ListMenuItem
//
//=============================================================================
override void Drawer(bool selected)
override void Draw(bool selected, ListMenuDescriptor desc)
{
if (mMode == 0 && !UpdatePlayerClass())
{
@ -241,12 +241,31 @@ class ListMenuItemPlayerDisplay : ListMenuItem
if (portrait != 'None' && !mNoportrait)
{
TextureID texid = TexMan.CheckForTexture(portrait, TexMan.Type_MiscPatch);
screen.DrawTexture (texid, true, mXpos, mYpos, DTA_Clean, true);
DrawTexture (desc, texid, mXpos, mYpos);
}
else
{
int x = int(mXpos - 160) * CleanXfac + (screen.GetWidth() >> 1);
int y = int(mYpos - 100) * CleanYfac + (screen.GetHeight() >> 1);
// Here we need to calculate the coordinates manually because Screen.DrawFrame only works in window coordinates and have to match the rest to it.
int x, y;
int w = desc.DisplayWidth();
int h = desc.DisplayHeight();
double sx, sy;
if (w == ListMenuDescriptor.CleanScale)
{
x = int(mXpos - 160) * CleanXfac + (screen.GetWidth() >> 1);
y = int(mYpos - 100) * CleanYfac + (screen.GetHeight() >> 1);
sx = CleanXfac;
sy = CleanYfac;
}
else
{
double fx, fy, fw, fh;
[fx, fy, fw, fh] = Screen.GetFullscreenRect(w, h, FSMode_ScaleToFit43);
sx = fw / w;
sy = fh / h;
x = int(fx + mXpos * sx);
y = int(fy + mYpos * sy);
}
int r = mBaseColor.r + mAddColor.r;
int g = mBaseColor.g + mAddColor.g;
@ -258,12 +277,12 @@ class ListMenuItemPlayerDisplay : ListMenuItem
Color c = Color(255, r, g, b);
screen.DrawTexture(mBackdrop, false, x, y - 1,
DTA_DestWidth, 72 * CleanXfac,
DTA_DestHeight, 80 * CleanYfac,
DTA_DestWidthF, 72. * sx,
DTA_DestHeightF, 80. * sy,
DTA_Color, c,
DTA_Masked, true);
Screen.DrawFrame (x, y, 72*CleanXfac, 80*CleanYfac-1);
Screen.DrawFrame (x, y, 72*sx, 80*sy-1);
if (mPlayerState != NULL)
{
@ -277,11 +296,11 @@ class ListMenuItemPlayerDisplay : ListMenuItem
{
int trans = mTranslate? Translation.MakeID(TRANSLATION_Players, MAXPLAYERS) : 0;
let tscale = TexMan.GetScaledSize(sprite);
Scale.X *= CleanXfac * tscale.X;
Scale.Y *= CleanYfac * tscale.Y;
Scale.X *= sx * tscale.X;
Scale.Y *= sy * tscale.Y;
screen.DrawTexture (sprite, false,
x + 36*CleanXfac, y + 71*CleanYfac,
x + 36*sx, y + 71*sy,
DTA_DestWidthF, Scale.X, DTA_DestHeightF, Scale.Y,
DTA_TranslationIndex, trans,
DTA_FlipX, flip);