- 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 // Draw parameter parsing
@ -460,6 +483,13 @@ bool SetTextureParms(F2DDrawer * drawer, DrawParms *parms, FGameTexture *img, do
parms->destheight = parms->texheight * CleanYfac; parms->destheight = parms->texheight * CleanYfac;
break; 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: case DTA_CleanNoMove:
parms->destwidth = parms->texwidth * CleanXfac; parms->destwidth = parms->texwidth * CleanXfac;
parms->destheight = parms->texheight * CleanYfac; parms->destheight = parms->texheight * CleanYfac;
@ -716,6 +746,7 @@ bool ParseDrawTextureTags(F2DDrawer *drawer, FGameTexture *img, double x, double
break; break;
case DTA_Clean: case DTA_Clean:
case DTA_CleanTop:
boolval = ListGetInt(tags); boolval = ListGetInt(tags);
if (boolval) if (boolval)
{ {

View file

@ -130,6 +130,7 @@ enum
DTA_Rotate, 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_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_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) static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc)
{ {
bool sizeset = false;
bool sizecompatible = true;
sc.MustGetStringName("{"); sc.MustGetStringName("{");
while (!sc.CheckString("}")) while (!sc.CheckString("}"))
{ {
@ -303,6 +305,7 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc)
sc.ScriptError("Unknown menu class '%s'", sc.String); sc.ScriptError("Unknown menu class '%s'", sc.String);
} }
desc->mClass = cls; desc->mClass = cls;
sizecompatible = false;
} }
else if (sc.Compare("Selector")) else if (sc.Compare("Selector"))
{ {
@ -382,14 +385,23 @@ static void ParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc)
{ {
desc->mVirtWidth = -1; desc->mVirtWidth = -1;
} }
if (sc.Compare("optclean")) else if (sc.Compare("optclean"))
{ {
desc->mVirtWidth = -2; desc->mVirtWidth = -2;
} }
else
{
sc.ScriptError("Invalid value '%s' for 'size'", sc.String);
}
} }
} }
else 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; bool success = false;
FStringf buildname("ListMenuItem%s", sc.String); FStringf buildname("ListMenuItem%s", sc.String);
PClass *cls = PClass::FindClass(buildname); 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) for (auto &p : desc->mItems)
{ {
GC::WriteBarrier(p); GC::WriteBarrier(p);

View file

@ -231,7 +231,6 @@ ListMenu "EpisodeMenu"
ListMenu "SkillMenu" ListMenu "SkillMenu"
{ {
IfGame(Doom, Chex) IfGame(Doom, Chex)
{ {
StaticPatch 96, 14, "M_NEWG", 0, "$MNU_NEWGAME" StaticPatch 96, 14, "M_NEWG", 0, "$MNU_NEWGAME"
@ -289,6 +288,7 @@ ListMenu "LoadGameMenu"
StaticTextCentered 160, -10, "$MNU_LOADGAME" StaticTextCentered 160, -10, "$MNU_LOADGAME"
Position 80,54 Position 80,54
Class "LoadMenu" // uses its own implementation Class "LoadMenu" // uses its own implementation
Size Clean
} }
//------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------
@ -302,6 +302,7 @@ ListMenu "SaveGameMenu"
StaticTextCentered 160, -10, "$MNU_SAVEGAME" StaticTextCentered 160, -10, "$MNU_SAVEGAME"
Position 80,54 Position 80,54
Class "SaveMenu" // uses its own implementation 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); 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 enum DrawTextureTags
{ {
TAG_USER = (1<<30), TAG_USER = (1<<30),
@ -208,6 +232,7 @@ enum DrawTextureTags
DTA_Rotate, 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_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_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 void ClearClipRect();
native static int, int, int, int GetClipRect(); native static int, int, int, int GetClipRect();
native static int, int, int, int GetViewWindow(); 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. // This is a leftover of the abandoned Inventory.DrawPowerup method.

View file

@ -2,7 +2,7 @@
class ListMenuDescriptor : MenuDescriptor native class ListMenuDescriptor : MenuDescriptor native
{ {
enum enum EScale
{ {
CleanScale = -1, CleanScale = -1,
OptCleanScale = -2 OptCleanScale = -2
@ -249,10 +249,10 @@ class ListMenu : Menu
{ {
for(int i=0;i<mDesc.mItems.Size(); i++) 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()) 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(); Super.Drawer();
} }

View file

@ -35,26 +35,54 @@
class ListMenuItem : MenuItemBase 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 (tex.isNull())
{ {
if ((Menu.MenuTime() % 8) < 6) if ((Menu.MenuTime() % 8) < 6)
{ {
screen.DrawText(ConFont, OptionMenuSettings.mFontColorSelection, DrawText(desc, ConFont, OptionMenuSettings.mFontColorSelection, mXpos + xofs, mYpos + yofs + 8, "\xd");
(mXpos + xofs - 160) * CleanXfac + screen.GetWidth() / 2,
(mYpos + yofs - 100) * CleanYfac + screen.GetHeight() / 2,
"\xd",
DTA_CellX, 8 * CleanXfac,
DTA_CellY, 8 * CleanYfac
);
} }
} }
else 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()) if (!mTexture.Exists())
{ {
return; return;
} }
let font = generic_ui? NewSmallFont : mFont;
double x = mXpos; double x = mXpos;
Vector2 vec = TexMan.GetScaledSize(mTexture); 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;
{ DrawTexture(desc, mTexture, x, abs(mYpos), mYpos < 0);
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);
}
} }
else else
{ {
x = (mXpos - 160) * CleanXfac + (Screen.GetWidth()>>1); let font = generic_ui ? NewSmallFont : mFont;
if (mSubstitute == "" || TexMan.OkForLocalization(mTexture, mSubstitute)) if (mCentered) x -= font.StringWidth(mSubstitute) / 2;
{ DrawText(desc, font, mColor, x, abs(mYpos), mSubstitute, mYpos < 0);
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);
}
} }
} }
} }
@ -162,25 +173,17 @@ class ListMenuItemStaticText : ListMenuItem
mCentered = centered; mCentered = centered;
} }
override void Drawer(bool selected) override void Draw(bool selected, ListMenuDescriptor desc)
{ {
if (mText.Length() != 0) if (mText.Length() != 0)
{ {
let font = generic_ui? NewSmallFont : mFont; let font = generic_ui? NewSmallFont : mFont;
String text = Stringtable.Localize(mText); String text = Stringtable.Localize(mText);
if (mYpos >= 0)
{ double x = mXpos;
double x = mXpos; if (mCentered) x -= font.StringWidth(text) / 2;
if (mCentered) x -= font.StringWidth(text)/2; DrawText(desc, font, mColor, x, abs(mYpos), text, mYpos < 0);
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);
}
} }
} }
} }
@ -288,10 +291,10 @@ class ListMenuItemTextItem : ListMenuItemSelectable
mHotkey = hotkey.GetNextCodePoint(0); mHotkey = hotkey.GetNextCodePoint(0);
} }
override void Drawer(bool selected) override void Draw(bool selected, ListMenuDescriptor desc)
{ {
let font = generic_ui? NewSmallFont : mFont; let font = generic_ui ? NewSmallFont : mFont;
screen.DrawText(font, selected ? mColorSelected : mColor, mXpos, mYpos, mText, DTA_Clean, true); DrawText(desc, font, selected ? mColorSelected : mColor, mXpos, mYpos, mText);
} }
override int GetWidth() override int GetWidth()
@ -325,9 +328,9 @@ class ListMenuItemPatchItem : ListMenuItemSelectable
mTexture = patch; 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() 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()) if (mMode == 0 && !UpdatePlayerClass())
{ {
@ -241,12 +241,31 @@ class ListMenuItemPlayerDisplay : ListMenuItem
if (portrait != 'None' && !mNoportrait) if (portrait != 'None' && !mNoportrait)
{ {
TextureID texid = TexMan.CheckForTexture(portrait, TexMan.Type_MiscPatch); TextureID texid = TexMan.CheckForTexture(portrait, TexMan.Type_MiscPatch);
screen.DrawTexture (texid, true, mXpos, mYpos, DTA_Clean, true); DrawTexture (desc, texid, mXpos, mYpos);
} }
else else
{ {
int x = int(mXpos - 160) * CleanXfac + (screen.GetWidth() >> 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 y = int(mYpos - 100) * CleanYfac + (screen.GetHeight() >> 1); 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 r = mBaseColor.r + mAddColor.r;
int g = mBaseColor.g + mAddColor.g; int g = mBaseColor.g + mAddColor.g;
@ -258,12 +277,12 @@ class ListMenuItemPlayerDisplay : ListMenuItem
Color c = Color(255, r, g, b); Color c = Color(255, r, g, b);
screen.DrawTexture(mBackdrop, false, x, y - 1, screen.DrawTexture(mBackdrop, false, x, y - 1,
DTA_DestWidth, 72 * CleanXfac, DTA_DestWidthF, 72. * sx,
DTA_DestHeight, 80 * CleanYfac, DTA_DestHeightF, 80. * sy,
DTA_Color, c, DTA_Color, c,
DTA_Masked, true); DTA_Masked, true);
Screen.DrawFrame (x, y, 72*CleanXfac, 80*CleanYfac-1); Screen.DrawFrame (x, y, 72*sx, 80*sy-1);
if (mPlayerState != NULL) if (mPlayerState != NULL)
{ {
@ -277,11 +296,11 @@ class ListMenuItemPlayerDisplay : ListMenuItem
{ {
int trans = mTranslate? Translation.MakeID(TRANSLATION_Players, MAXPLAYERS) : 0; int trans = mTranslate? Translation.MakeID(TRANSLATION_Players, MAXPLAYERS) : 0;
let tscale = TexMan.GetScaledSize(sprite); let tscale = TexMan.GetScaledSize(sprite);
Scale.X *= CleanXfac * tscale.X; Scale.X *= sx * tscale.X;
Scale.Y *= CleanYfac * tscale.Y; Scale.Y *= sy * tscale.Y;
screen.DrawTexture (sprite, false, 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_DestWidthF, Scale.X, DTA_DestHeightF, Scale.Y,
DTA_TranslationIndex, trans, DTA_TranslationIndex, trans,
DTA_FlipX, flip); DTA_FlipX, flip);