qzdoom-gpl/wadsrc/static/zscript/menu/optionmenuitems.txt
Christoph Oelckers 6525e04118 - added restrictions to CVAR and CCMD access functions for the menus. CVAR changes are only allowed when the menu is open or for mod-CVARs. The CCMD execution function is now private to the control requiring it and heavily guarded against improper access from the outside so that abuse is mostly impossible.
This also means that the remaining scriptification of the menu is on hold. The player menu would require even more access to critical game data, which is a no-go, and the other remaining menus offer little benefit from getting scriptified.
2017-02-13 19:18:45 +01:00

1208 lines
29 KiB
Text

/*
** optionmenuitems.txt
** Control items for option menus
**
**---------------------------------------------------------------------------
** Copyright 2010-2017 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
class OptionMenuItem : MenuItemBase
{
String mLabel;
bool mCentered;
protected void Init(String label, String command, bool center = false)
{
Super.Init(0, 0, command);
mLabel = label;
mCentered = center;
}
protected void drawLabel(int indent, int y, int color, bool grayed = false)
{
String label = Stringtable.Localize(mLabel);
int overlay = grayed? Color(96,48,0,0) : 0;
int x;
int w = SmallFont.StringWidth(label) * CleanXfac_1;
if (!mCentered) x = indent - w;
else x = (screen.GetWidth() - w) / 2;
screen.DrawText (SmallFont, color, x, y, label, DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay);
}
int CursorSpace()
{
return (14 * CleanXfac_1);
}
override bool Selectable()
{
return true;
}
override int GetIndent()
{
if (mCentered) return 0;
return SmallFont.StringWidth(Stringtable.Localize(mLabel));
}
override bool MouseEvent(int type, int x, int y)
{
if (Selectable() && type == Menu.MOUSE_Release)
{
return Menu.GetCurrentMenu().MenuEvent(Menu.MKEY_Enter, true);
}
return false;
}
}
//=============================================================================
//
// opens a submenu, command is a submenu name
//
//=============================================================================
class OptionMenuItemSubmenu : OptionMenuItem
{
int mParam;
OptionMenuItemSubmenu Init(String label, Name command, int param = 0)
{
Super.init(label, command);
mParam = param;
return self;
}
override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected)
{
drawLabel(indent, y, selected? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColorMore);
return indent;
}
override bool Activate()
{
Menu.MenuSound("menu/choose");
Menu.SetMenu(mAction, mParam);
return true;
}
}
//=============================================================================
//
// Executes a CCMD, command is a CCMD name
//
//=============================================================================
class OptionMenuItemCommand : OptionMenuItemSubmenu
{
OptionMenuItemCommand Init(String label, Name command)
{
Super.Init(label, command);
return self;
}
private native static void DoCommand(String cmd); // This is very intentionally limited to this menu item to prevent abuse.
override bool Activate()
{
// This needs to perform a few checks to prevent abuse by malicious modders.
let m = Menu.GetCurrentMenu();
// don't execute if no menu is active
if (m == null) return false;
// don't execute if this item cannot be found in the current menu.
if (m.GetItem(mAction) != self) return false;
Menu.MenuSound("menu/choose");
Console.DoCommand(mAction);
return true;
}
}
//=============================================================================
//
// Executes a CCMD after confirmation, command is a CCMD name
//
//=============================================================================
class OptionMenuItemSafeCommand : OptionMenuItemCommand
{
String mPrompt;
OptionMenuItemSafeCommand Init(String label, Name command, String prompt = "")
{
Super.Init(label, command);
mPrompt = prompt;
return self;
}
override bool MenuEvent (int mkey, bool fromcontroller)
{
if (mkey == Menu.MKEY_MBYes)
{
Super.Activate(mKey, fromController);
return true;
}
return Super.MenuEvent(mkey, fromcontroller);
}
override bool Activate()
{
String msg = mPrompt.Length() > 0 ? mPrompt : "$SAFEMESSAGE";
msg = StringTable.Localize(msg);
String actionLabel = StringTable.localize(mLabel);
String FullString;
FullString = String.Format("%s%s%s\n\n%s", TEXTCOLOR_WHITE, actionLabel, TEXTCOLOR_NORMAL, msg);
Menu.StartMessage(FullString, 0);
return true;
}
}
//=============================================================================
//
// Base class for option lists
//
//=============================================================================
class OptionMenuItemOptionBase : OptionMenuItem
{
// command is a CVAR
Name mValues; // Entry in OptionValues table
CVar mGrayCheck;
int mCenter;
const OP_VALUES = 0x11001;
protected void Init(String label, Name command, Name values, CVar graycheck, int center)
{
Super.Init(label, command);
mValues = values;
mGrayCheck = graycheck;
mCenter = center;
}
override bool SetString(int i, String newtext)
{
if (i == OP_VALUES)
{
int cnt = OptionValues.GetCount(mValues);
if (cnt >= 0)
{
mValues = newtext;
int s = GetSelection();
if (s >= cnt) s = 0;
SetSelection(s); // readjust the CVAR if its value is outside the range now
return true;
}
}
return false;
}
//=============================================================================
virtual int GetSelection()
{
return 0;
}
virtual void SetSelection(int Selection)
{
}
//=============================================================================
override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected)
{
bool grayed = mGrayCheck != null && !(mGrayCheck.GetInt());
if (mCenter)
{
indent = (screen.GetWidth() / 2);
}
drawLabel(indent, y, selected? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor, grayed);
int overlay = grayed? Color(96,48,0,0) : 0;
int Selection = GetSelection();
String text = StringTable.Localize(OptionValues.GetText(mValues, Selection));
if (text.Length() == 0) text = "Unknown";
screen.DrawText (SmallFont, OptionMenuSettings.mFontColorValue, indent + CursorSpace(), y, text, DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay);
return indent;
}
//=============================================================================
override bool MenuEvent (int mkey, bool fromcontroller)
{
int cnt = OptionValues.GetCount(mValues);
if (cnt > 0)
{
int Selection = GetSelection();
if (mkey == Menu.MKEY_Left)
{
if (Selection == -1) Selection = 0;
else if (--Selection < 0) Selection = cnt - 1;
}
else if (mkey == Menu.MKEY_Right || mkey == Menu.MKEY_Enter)
{
if (++Selection >= cnt) Selection = 0;
}
else
{
return Super.MenuEvent(mkey, fromcontroller);
}
SetSelection(Selection);
Menu.MenuSound("menu/change");
}
else
{
return Super.MenuEvent(mkey, fromcontroller);
}
return true;
}
override bool Selectable()
{
return mGrayCheck == null || mGrayCheck.GetInt();
}
}
//=============================================================================
//
// Change a CVAR, command is the CVAR name
//
//=============================================================================
class OptionMenuItemOption : OptionMenuItemOptionBase
{
CVar mCVar;
OptionMenuItemOption Init(String label, Name command, Name values, CVar graycheck = null, int center = 0)
{
Super.Init(label, command, values, graycheck, center);
mCVar = CVar.FindCVar(mAction);
return self;
}
//=============================================================================
override int GetSelection()
{
int Selection = -1;
int cnt = OptionValues.GetCount(mValues);
if (cnt > 0 && mCVar != null)
{
if (OptionValues.GetTextValue(mValues, 0).Length() == 0)
{
let f = mCVar.GetFloat();
for(int i = 0; i < cnt; i++)
{
if (f ~== OptionValues.GetValue(mValues, i))
{
Selection = i;
break;
}
}
}
else
{
String cv = mCVar.GetString();
for(int i = 0; i < cnt; i++)
{
if (cv ~== OptionValues.GetTextValue(mValues, i))
{
Selection = i;
break;
}
}
}
}
return Selection;
}
override void SetSelection(int Selection)
{
int cnt = OptionValues.GetCount(mValues);
if (cnt > 0 && mCVar != null)
{
if (OptionValues.GetTextValue(mValues, 0).Length() == 0)
{
mCVar.SetFloat(OptionValues.GetValue(mValues, Selection));
}
else
{
mCVar.SetString(OptionValues.GetTextValue(mValues, Selection));
}
}
}
}
//=============================================================================
//
// This class is used to capture the key to be used as the new key binding
// for a control item
//
//=============================================================================
class EnterKey : Menu
{
OptionMenuItemControlBase mOwner;
void Init(Menu parent, OptionMenuItemControlBase owner)
{
Super.Init(parent);
mOwner = owner;
SetMenuMessage(1);
menuactive = Menu.WaitKey; // There should be a better way to disable GUI capture...
}
override bool TranslateKeyboardEvents()
{
return false;
}
private void SetMenuMessage(int which)
{
let parent = OptionMenu(mParentMenu);
if (parent != null)
{
let it = parent.GetItem('Controlmessage');
if (it != null)
{
it.SetValue(0, which);
}
}
}
override bool Responder(InputEvent ev)
{
// This checks raw keys, not GUI keys.
if (ev.type == InputEvent.KeyDown)
{
mOwner.SendKey(ev.data1);
menuactive = Menu.On;
SetMenuMessage(0);
Close();
mParentMenu.MenuEvent((ev.data1 == InputEvent.KEY_ESCAPE)? Menu.MKEY_Abort : Menu.MKEY_Input, 0);
return true;
}
return false;
}
override void Drawer()
{
mParentMenu.Drawer();
}
}
//=============================================================================
//
// // Edit a key binding, Action is the CCMD to bind
//
//=============================================================================
class OptionMenuItemControlBase : OptionMenuItem
{
KeyBindings mBindings;
int mInput;
bool mWaiting;
protected void Init(String label, Name command, KeyBindings bindings)
{
Super.init(label, command);
mBindings = bindings;
mWaiting = false;
}
//=============================================================================
override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected)
{
drawLabel(indent, y, mWaiting? OptionMenuSettings.mFontColorHighlight:
(selected? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor));
String description;
int Key1, Key2;
[Key1, Key2] = mBindings.GetKeysForCommand(mAction);
description = KeyBindings.NameKeys (Key1, Key2);
if (description.Length() > 0)
{
Menu.DrawConText(Font.CR_WHITE, indent + CursorSpace(), y + (OptionMenuSettings.mLinespacing-8)*CleanYfac_1, description);
}
else
{
screen.DrawText(SmallFont, Font.CR_BLACK, indent + CursorSpace(), y + (OptionMenuSettings.mLinespacing-8)*CleanYfac_1, "---", DTA_CleanNoMove_1, true);
}
return indent;
}
//=============================================================================
override bool MenuEvent(int mkey, bool fromcontroller)
{
if (mkey == Menu.MKEY_Input)
{
mWaiting = false;
mBindings.SetBind(mInput, mAction);
return true;
}
else if (mkey == Menu.MKEY_Clear)
{
mBindings.UnbindACommand(mAction);
return true;
}
else if (mkey == Menu.MKEY_Abort)
{
mWaiting = false;
return true;
}
return false;
}
void SendKey(int key)
{
mInput = key;
}
override bool Activate()
{
Menu.MenuSound("menu/choose");
mWaiting = true;
let input = new("EnterKey");
input.Init(Menu.GetCurrentMenu(), self);
input.ActivateMenu();
return true;
}
}
class OptionMenuItemControl : OptionMenuItemControlBase
{
OptionMenuItemControl Init(String label, Name command)
{
Super.Init(label, command, Bindings);
return self;
}
}
class OptionMenuItemMapControl : OptionMenuItemControlBase
{
OptionMenuItemMapControl Init(String label, Name command)
{
Super.Init(label, command, AutomapBindings);
return self;
}
}
//=============================================================================
//
//
//
//=============================================================================
class OptionMenuItemStaticText : OptionMenuItem
{
int mColor;
// this function is only for use from MENUDEF, it needs to do some strange things with the color for backwards compatibility.
OptionMenuItemStaticText Init(String label, int cr = -1)
{
Super.Init(label, 'None', true);
mColor = OptionMenuSettings.mFontColor;
if ((cr & 0xffff0000) == 0x12340000) mColor = cr & 0xffff;
else if (cr > 0) mColor = OptionMenuSettings.mFontColorHeader;
return self;
}
OptionMenuItemStaticText InitDirect(String label, int cr)
{
Super.Init(label, 'None', true);
mColor = cr;
return self;
}
override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected)
{
drawLabel(indent, y, mColor);
return -1;
}
override bool Selectable()
{
return false;
}
}
//=============================================================================
//
//
//
//=============================================================================
class OptionMenuItemStaticTextSwitchable : OptionMenuItem
{
int mColor;
String mAltText;
int mCurrent;
// this function is only for use from MENUDEF, it needs to do some strange things with the color for backwards compatibility.
OptionMenuItemStaticTextSwitchable Init(String label, String label2, Name command, int cr = -1)
{
Super.Init(label, command, true);
mAltText = label2;
mCurrent = 0;
mColor = OptionMenuSettings.mFontColor;
if ((cr & 0xffff0000) == 0x12340000) mColor = cr & 0xffff;
else if (cr > 0) mColor = OptionMenuSettings.mFontColorHeader;
return self;
}
OptionMenuItemStaticTextSwitchable InitDirect(String label, String label2, Name command, int cr)
{
Super.Init(label, command, true);
mColor = cr;
mAltText = label2;
mCurrent = 0;
return self;
}
override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected)
{
String txt = StringTable.Localize(mCurrent? mAltText : mLabel);
int w = SmallFont.StringWidth(txt) * CleanXfac_1;
int x = (screen.GetWidth() - w) / 2;
screen.DrawText (SmallFont, mColor, x, y, txt, DTA_CleanNoMove_1, true);
return -1;
}
override bool SetValue(int i, int val)
{
if (i == 0)
{
mCurrent = val;
return true;
}
return false;
}
override bool SetString(int i, String newtext)
{
if (i == 0)
{
mAltText = newtext;
return true;
}
return false;
}
override bool Selectable()
{
return false;
}
}
//=============================================================================
//
//
//
//=============================================================================
class OptionMenuSliderBase : OptionMenuItem
{
// command is a CVAR
double mMin, mMax, mStep;
int mShowValue;
int mDrawX;
int mSliderShort;
protected void Init(String label, double min, double max, double step, int showval)
{
Super.Init(label, 'None');
mMin = min;
mMax = max;
mStep = step;
mShowValue = showval;
mDrawX = 0;
mSliderShort = 0;
}
virtual double GetSliderValue()
{
return 0;
}
virtual void SetSliderValue(double val)
{
}
//=============================================================================
//
// Draw a slider. Set fracdigits negative to not display the current value numerically.
//
//=============================================================================
private void DrawSlider (int x, int y, double min, double max, double cur, int fracdigits, int indent)
{
String formater = String.format("%%.%df", fracdigits); // The format function cannot do the '%.*f' syntax.
String textbuf;
double range;
int maxlen = 0;
int right = x + (12*8 + 4) * CleanXfac_1;
int cy = y + (OptionMenuSettings.mLinespacing-8)*CleanYfac_1;
range = max - min;
double ccur = clamp(cur, min, max) - min;
if (fracdigits >= 0)
{
textbuf = String.format(formater, max);
maxlen = SmallFont.StringWidth(textbuf) * CleanXfac_1;
}
mSliderShort = right + maxlen > screen.GetWidth();
if (!mSliderShort)
{
Menu.DrawConText(Font.CR_WHITE, x, cy, "\x10\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x12");
Menu.DrawConText(Font.CR_ORANGE, x + int((5 + ((ccur * 78) / range)) * CleanXfac_1), cy, "\x13");
}
else
{
// On 320x200 we need a shorter slider
Menu.DrawConText(Font.CR_WHITE, x, cy, "\x10\x11\x11\x11\x11\x11\x12");
Menu.DrawConText(Font.CR_ORANGE, x + int((5 + ((ccur * 38) / range)) * CleanXfac_1), cy, "\x13");
right -= 5*8*CleanXfac_1;
}
if (fracdigits >= 0 && right + maxlen <= screen.GetWidth())
{
textbuf = String.format(formater, cur);
screen.DrawText(SmallFont, Font.CR_DARKGRAY, right, y, textbuf, DTA_CleanNoMove_1, true);
}
}
//=============================================================================
override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected)
{
drawLabel(indent, y, selected? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor);
mDrawX = indent + CursorSpace();
DrawSlider (mDrawX, y, mMin, mMax, GetSliderValue(), mShowValue, indent);
return indent;
}
//=============================================================================
override bool MenuEvent (int mkey, bool fromcontroller)
{
double value = GetSliderValue();
if (mkey == Menu.MKEY_Left)
{
value -= mStep;
}
else if (mkey == Menu.MKEY_Right)
{
value += mStep;
}
else
{
return OptionMenuItem.MenuEvent(mkey, fromcontroller);
}
if (value ~== 0) value = 0; // This is to prevent formatting anomalies with very small values
SetSliderValue(clamp(value, mMin, mMax));
Menu.MenuSound("menu/change");
return true;
}
override bool MouseEvent(int type, int x, int y)
{
let lm = OptionMenu(Menu.GetCurrentMenu());
if (type != Menu.MOUSE_Click)
{
if (!lm.CheckFocus(self)) return false;
}
if (type == Menu.MOUSE_Release)
{
lm.ReleaseFocus();
}
int slide_left = mDrawX+8*CleanXfac_1;
int slide_right = slide_left + (10*8*CleanXfac_1 >> mSliderShort); // 12 char cells with 8 pixels each.
if (type == Menu.MOUSE_Click)
{
if (x < slide_left || x >= slide_right) return true;
}
x = clamp(x, slide_left, slide_right);
double v = mMin + ((x - slide_left) * (mMax - mMin)) / (slide_right - slide_left);
if (v != GetSliderValue())
{
SetSliderValue(v);
//Menu.MenuSound("menu/change");
}
if (type == Menu.MOUSE_Click)
{
lm.SetFocus(self);
}
return true;
}
}
//=============================================================================
//
//
//
//=============================================================================
class OptionMenuItemSlider : OptionMenuSliderBase
{
CVar mCVar;
OptionMenuItemSlider Init(String label, Name command, double min, double max, double step, int showval = 1)
{
Super.Init(label, min, max, step, showval);
mCVar =CVar.FindCVar(command);
return self;
}
override double GetSliderValue()
{
if (mCVar != null)
{
return mCVar.GetFloat();
}
else
{
return 0;
}
}
override void SetSliderValue(double val)
{
if (mCVar != null)
{
mCVar.SetFloat(val);
}
}
}
//=============================================================================
//
// // Edit a key binding, Action is the CCMD to bind
//
//=============================================================================
class OptionMenuItemColorPicker : OptionMenuItem
{
CVar mCVar;
const CPF_RESET = 0x20001;
OptionMenuItemColorPicker Init(String label, Name command)
{
Super.Init(label, command);
CVar cv = CVar.FindCVar(command);
if (cv != null && cv.GetRealType() != CVar.CVAR_Color) cv = null;
mCVar = cv;
return self;
}
//=============================================================================
override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected)
{
drawLabel(indent, y, selected? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor);
if (mCVar != null)
{
int box_x = indent + CursorSpace();
int box_y = y + CleanYfac_1;
screen.Clear (box_x, box_y, box_x + 32*CleanXfac_1, box_y + OptionMenuSettings.mLinespacing*CleanYfac_1, mCVar.GetInt() | 0xff000000);
}
return indent;
}
override bool SetValue(int i, int v)
{
if (i == CPF_RESET && mCVar != null)
{
mCVar.ResetToDefault();
return true;
}
return false;
}
override bool Activate()
{
if (mCVar != null)
{
Menu.MenuSound("menu/choose");
// This code is a bit complicated because it should allow subclassing the
// colorpicker menu.
// New color pickers must inherit from the internal one to work here.
let desc = MenuDescriptor.GetDescriptor('Colorpickermenu');
if (desc != NULL && (desc.mClass == null || desc.mClass is "ColorPickerMenu"))
{
let odesc = OptionMenuDescriptor(desc);
if (odesc != null)
{
let cls = desc.mClass;
if (cls == null) cls = "ColorpickerMenu";
let picker = ColorpickerMenu(new(cls));
picker.Init(Menu.GetCurrentMenu(), mLabel, odesc, mCVar);
picker.ActivateMenu();
return true;
}
}
}
return false;
}
}
class OptionMenuItemScreenResolution : OptionMenuItem
{
String mResTexts[3];
int mSelection;
int mHighlight;
int mMaxValid;
enum EValues
{
SRL_INDEX = 0x30000,
SRL_SELECTION = 0x30003,
SRL_HIGHLIGHT = 0x30004,
};
OptionMenuItemScreenResolution Init(String command)
{
Super.Init("", command);
mSelection = 0;
mHighlight = -1;
return self;
}
override bool SetValue(int i, int v)
{
if (i == SRL_SELECTION)
{
mSelection = v;
return true;
}
else if (i == SRL_HIGHLIGHT)
{
mHighlight = v;
return true;
}
return false;
}
override bool, int GetValue(int i)
{
if (i == SRL_SELECTION)
{
return true, mSelection;
}
return false, 0;
}
override bool SetString(int i, String newtext)
{
if (i >= SRL_INDEX && i <= SRL_INDEX+2)
{
mResTexts[i-SRL_INDEX] = newtext;
if (mResTexts[0].Length() == 0) mMaxValid = -1;
else if (mResTexts[1].Length() == 0) mMaxValid = 0;
else if (mResTexts[2].Length() == 0) mMaxValid = 1;
else mMaxValid = 2;
return true;
}
return false;
}
override bool, String GetString(int i)
{
if (i >= SRL_INDEX && i <= SRL_INDEX+2)
{
return true, mResTexts[i-SRL_INDEX];
}
return false, "";
}
override bool MenuEvent (int mkey, bool fromcontroller)
{
if (mkey == Menu.MKEY_Left)
{
if (--mSelection < 0) mSelection = mMaxValid;
Menu.MenuSound ("menu/cursor");
return true;
}
else if (mkey == Menu.MKEY_Right)
{
if (++mSelection > mMaxValid) mSelection = 0;
Menu.MenuSound ("menu/cursor");
return true;
}
else
{
return Super.MenuEvent(mkey, fromcontroller);
}
return false;
}
override bool MouseEvent(int type, int x, int y)
{
int colwidth = screen.GetWidth() / 3;
mSelection = x / colwidth;
return Super.MouseEvent(type, x, y);
}
override bool Activate()
{
Menu.MenuSound("menu/choose");
Menu.SetVideoMode();
return true;
}
override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected)
{
int colwidth = screen.GetWidth() / 3;
int col;
for (int x = 0; x < 3; x++)
{
if (selected && mSelection == x)
col = OptionMenuSettings.mFontColorSelection;
else if (x == mHighlight)
col = OptionMenuSettings.mFontColorHighlight;
else
col = OptionMenuSettings.mFontColorValue;
screen.DrawText (SmallFont, col, colwidth * x + 20 * CleanXfac_1, y, mResTexts[x], DTA_CleanNoMove_1, true);
}
return colwidth * mSelection + 20 * CleanXfac_1 - CursorSpace();
}
override bool Selectable()
{
return mMaxValid >= 0;
}
override void Ticker()
{
if (Selectable() && mSelection > mMaxValid)
{
mSelection = mMaxValid;
}
}
}
//=============================================================================
//
// [TP] OptionMenuFieldBase
//
// Base class for input fields
//
//=============================================================================
class OptionMenuFieldBase : OptionMenuItem
{
void Init (String label, Name command, CVar graycheck = null)
{
Super.Init(label, command);
mCVar = CVar.FindCVar(mAction);
mGrayCheck = graycheck;
}
String GetCVarString()
{
if (mCVar == null)
return "";
return mCVar.GetString();
}
virtual String Represent()
{
return GetCVarString();
}
override int Draw (OptionMenuDescriptor d, int y, int indent, bool selected)
{
bool grayed = mGrayCheck != null && !mGrayCheck.GetInt();
drawLabel(indent, y, selected ? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor, grayed);
int overlay = grayed? Color(96, 48, 0, 0) : 0;
screen.DrawText(SmallFont, OptionMenuSettings.mFontColorValue, indent + CursorSpace(), y, Represent(), DTA_CleanNoMove_1, true, DTA_ColorOverlay, overlay);
return indent;
}
override bool, String GetString (int i)
{
if (i == 0)
{
return true, GetCVarString();
}
return false, "";
}
override bool SetString (int i, String s)
{
if (i == 0)
{
if (mCVar) mCVar.SetString(s);
return true;
}
return false;
}
CVar mCVar;
CVar mGrayCheck;
}
//=============================================================================
//
// [TP] OptionMenuTextField
//
// A text input field widget, for use with string CVars.
//
//=============================================================================
class OptionMenuItemTextField : OptionMenuFieldBase
{
TextEnterMenu mEnter;
OptionMenuItemTextField Init (String label, Name command, CVar graycheck = null)
{
Super.Init(label, command, graycheck);
mEnter = null;
return self;
}
override String Represent()
{
if (mEnter) return mEnter.GetText() .. SmallFont.GetCursor();
else return GetCVarString();
}
override int Draw(OptionMenuDescriptor desc, int y, int indent, bool selected)
{
if (mEnter)
{
// reposition the text so that the cursor is visible when in entering mode.
String text = Represent();
int tlen = SmallFont.StringWidth(text) * CleanXfac_1;
int newindent = screen.GetWidth() - tlen - CursorSpace();
if (newindent < indent) indent = newindent;
}
return Super.Draw(desc, y, indent, selected);
}
override bool MenuEvent (int mkey, bool fromcontroller)
{
if (mkey == Menu.MKEY_Enter)
{
Menu.MenuSound("menu/choose");
mEnter = TextEnterMenu.Open(Menu.GetCurrentMenu(), GetCVarString(), -1, 2, fromcontroller);
return true;
}
else if (mkey == Menu.MKEY_Input)
{
if (mCVar) mCVar.SetString(mEnter.GetText());
mEnter = null;
return true;
}
else if (mkey == Menu.MKEY_Abort)
{
mEnter = null;
return true;
}
return Super.MenuEvent(mkey, fromcontroller);
}
}
//=============================================================================
//
// [TP] FOptionMenuNumberField
//
// A numeric input field widget, for use with number CVars where sliders are inappropriate (i.e.
// where the user is interested in the exact value specifically)
//
//=============================================================================
class OptionMenuItemNumberField : OptionMenuFieldBase
{
OptionMenuItemNumberField Init (String label, Name command, float minimum = 0, float maximum = 100, float step = 1, CVar graycheck = null)
{
Super.Init(label, command, graycheck);
mMinimum = min(minimum, maximum);
mMaximum = max(minimum, maximum);
mStep = max(1, step);
return self;
}
override String Represent()
{
if (mCVar == null) return "";
return String.format("%.3f", mCVar.GetFloat());
}
override bool MenuEvent (int mkey, bool fromcontroller)
{
if (mCVar)
{
float value = mCVar.GetFloat();
if (mkey == Menu.MKEY_Left)
{
value -= mStep;
if (value < mMinimum) value = mMaximum;
}
else if (mkey == Menu.MKEY_Right || mkey == Menu.MKEY_Enter)
{
value += mStep;
if (value > mMaximum) value = mMinimum;
}
else
return Super.MenuEvent(mkey, fromcontroller);
mCVar.SetFloat(value);
Menu.MenuSound("menu/change");
}
return true;
}
float mMinimum;
float mMaximum;
float mStep;
}