/* ** 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 = OptionMenu(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"); 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(); 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 OnInputEvent(InputEvent ev) { // This menu checks raw keys, not GUI keys because it needs the raw codes for binding. if (ev.type == InputEvent.Type_KeyDown) { mOwner.SendKey(ev.KeyScan); menuactive = Menu.On; SetMenuMessage(0); Close(); mParentMenu.MenuEvent((ev.KeyScan == 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); mEnter.ActivateMenu(); 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"); } else return Super.MenuEvent(mkey, fromcontroller); return true; } float mMinimum; float mMaximum; float mStep; }