User:Egocarib/MCM Utility Scripts
MCM Utility Scripts[edit | edit source]
A few handy script fragments I have used in my MCM menus.
SetStarterInfoText[edit | edit source]
Sets the info text on a menu page when the user first selects it, before any options are highlighted. Call the function while you're building the page options.
Syntax[edit | edit source]
- SetStarterInfoText(string desc)
Example[edit | edit source]
if kPage == "Settings"
SetTitleText("Settings Page")
SetStarterInfoText("Choose a Setting to Adjust")
SetCursorPosition(0)
SetCursorFillMode(TOP_TO_BOTTOM)
;...
Code[edit | edit source]
;Description text setter, allows display of info text immediately when page is first selected:
Function setStarterInfoText(string desc)
registerForModEvent("_setPageStartDescrip", "OnPageDescriptionSet")
sendModEvent("_setPageStartDescrip", desc, 0)
EndFunction
Event OnPageDescriptionSet(string eventName, string desc, float iterations, Form sender)
int readyState = UI.GetInt("Journal Menu", "_global.ConfigPanel.READY")
int curState = UI.GetInt("Journal Menu", "_root.ConfigPanelFader.configPanel._state")
if (curState == readyState || iterations > 10.0) ;safety limiter
UI.InvokeString("Journal Menu", "_root.ConfigPanelFader.configPanel.setInfoText", desc)
UnregisterForModEvent("_setPageStartDescrip")
else
utility.waitmenumode(0.05)
SendModEvent("_setPageStartDescrip", desc, iterations + 1.0)
endif
EndEvent
RefreshPage[edit | edit source]
Page resetter that maintains highlight focus on currently selected option (because forcePageReset() by itself will lose focus on the currently highlighted option). I find this useful if the user selecting an option alters other options on the page as well, and full page refresh is needed.
Syntax[edit | edit source]
- RefreshPage()
Example[edit | edit source]
Event OnOptionSelect(int option)
if option == myOp1
myBool = !myBool
RefreshPage()
;...
Code[edit | edit source]
;Page Refresh that maintains highlight on currently selected option:
Function RefreshPage()
int index = UI.GetInt("Journal Menu", "_root.ConfigPanelFader.configPanel.contentHolder.optionsPanel.optionsList.selectedIndex")
registerForModEvent("_pageRefresh", "OnPageRefreshed")
sendModEvent("_pageRefresh", index as string, 0)
forcePageReset()
EndFunction
Event OnPageRefreshed(string eventName, string strArg, float iterations, Form sender)
int returnPlace = strArg as int
int readyState = UI.GetInt("Journal Menu", "_global.ConfigPanel.READY")
int curState = UI.GetInt("Journal Menu", "_root.ConfigPanelFader.configPanel._state")
if (curState == readyState || iterations > 10.0)
UI.SetInt("Journal Menu", "_root.ConfigPanelFader.configPanel.contentHolder.optionsPanel.optionsList.selectedIndex", returnPlace)
UnregisterForModEvent("_pageRefresh")
else
utility.wait(0.04)
SendModEvent("_pageRefresh", strArg, iterations + 1.0)
endif
EndEvent
MCM Text Input[edit | edit source]
NOTICE: I would no longer recommend using this method, since better methods of text input have now been made easily available. | ||
A method of allowing text input from users in the MCM menu by listening for keypresses in conjunction with a refreshing messagebox. Sounds wonky but works surprisingly well, as long as your users don't need to enter more than a few words or so (renaming items or presets, typing a string to search for, etc). I couldn't figure out how to inject my own text input swf, so I used this papyrus workaround instead.
The example currently interprets all letters as uppercase (which was good enough for my purpose), but it would be quite easy to add lower-case too by just adding the appropriate DXScanCodes and checking if the shift key is pressed as you're listening for letters. You'll notice I've also added extra spaces to all the strings to avoid papyrus string caching inconsistencies (papyrus changing the case of letters/strings unexpectedly).
In the following example, the messagebox is designed to pop up when a user selects a simple MCM option, asking them to type in their text.
Fragment 1 -- Key Arrays[edit | edit source]
Make sure to call initializeKeyCodes() before the user would use your text input option (e.g. during onConfigInit())
;key init for the input feature
Function initializeKeyCodes()
;list of basic alphanumeric character keys
DXScanCodes = new int[51]
DXScanCodes[0] = 16 ;begin alphabet letters
DXScanCodes[1] = 17
DXScanCodes[2] = 18
DXScanCodes[3] = 19
DXScanCodes[4] = 20
DXScanCodes[5] = 21
DXScanCodes[6] = 22
DXScanCodes[7] = 23
DXScanCodes[8] = 24
DXScanCodes[9] = 25
DXScanCodes[10] = 30
DXScanCodes[11] = 31
DXScanCodes[12] = 32
DXScanCodes[13] = 33
DXScanCodes[14] = 34
DXScanCodes[15] = 35
DXScanCodes[16] = 36
DXScanCodes[17] = 37
DXScanCodes[18] = 38
DXScanCodes[19] = 44
DXScanCodes[20] = 45
DXScanCodes[21] = 46
DXScanCodes[22] = 47
DXScanCodes[23] = 48
DXScanCodes[24] = 49
DXScanCodes[25] = 50 ;end alphabet letters
DXScanCodes[26] = 2
DXScanCodes[27] = 3
DXScanCodes[28] = 4
DXScanCodes[29] = 5
DXScanCodes[30] = 6
DXScanCodes[31] = 7
DXScanCodes[32] = 8
DXScanCodes[33] = 9
DXScanCodes[34] = 10
DXScanCodes[35] = 11
DXScanCodes[36] = 12
DXScanCodes[37] = 40
DXScanCodes[38] = 57 ;spacebar
DXScanCodes[39] = 71
DXScanCodes[40] = 72
DXScanCodes[41] = 73
DXScanCodes[42] = 74
DXScanCodes[43] = 75
DXScanCodes[44] = 76
DXScanCodes[45] = 77
DXScanCodes[46] = 78
DXScanCodes[47] = 79
DXScanCodes[48] = 80
DXScanCodes[49] = 81
DXScanCodes[50] = 82
;characters correlated to Codes above:
;(extra space added to try and avoid papyrus string caching inconsistencies)
DXScanChars = new string[51]
DXScanChars[0] = "Q "
DXScanChars[1] = "W "
DXScanChars[2] = "E "
DXScanChars[3] = "R "
DXScanChars[4] = "T "
DXScanChars[5] = "Y "
DXScanChars[6] = "U "
DXScanChars[7] = "I "
DXScanChars[8] = "O "
DXScanChars[9] = "P "
DXScanChars[10] = "A "
DXScanChars[11] = "S "
DXScanChars[12] = "D "
DXScanChars[13] = "F "
DXScanChars[14] = "G "
DXScanChars[15] = "H "
DXScanChars[16] = "J "
DXScanChars[17] = "K "
DXScanChars[18] = "L "
DXScanChars[19] = "Z "
DXScanChars[20] = "X "
DXScanChars[21] = "C "
DXScanChars[22] = "V "
DXScanChars[23] = "B "
DXScanChars[24] = "N "
DXScanChars[25] = "M "
DXScanChars[26] = "1 "
DXScanChars[27] = "2 "
DXScanChars[28] = "3 "
DXScanChars[29] = "4 "
DXScanChars[30] = "5 "
DXScanChars[31] = "6 "
DXScanChars[32] = "7 "
DXScanChars[33] = "8 "
DXScanChars[34] = "9 "
DXScanChars[35] = "0 "
DXScanChars[36] = "- "
DXScanChars[37] = "' "
DXScanChars[38] = " " ;spacebar
DXScanChars[39] = "7 "
DXScanChars[40] = "8 "
DXScanChars[41] = "9 "
DXScanChars[42] = "- "
DXScanChars[43] = "4 "
DXScanChars[44] = "5 "
DXScanChars[45] = "6 "
DXScanChars[46] = "+ "
DXScanChars[47] = "1 "
DXScanChars[48] = "2 "
DXScanChars[49] = "3 "
DXScanChars[50] = "0 "
EndFunction
Fragment 2 -- The Option Handler[edit | edit source]
;////////////// INPUT OPTION /////////////////;
;...
elseif o == presetRenameOp ;option allowing user to rename a preset
int letterCounter
displayString = " " ;I add a starting space to avoid any possible papyrus string caching problems (papyrus changing the case of letters)
currentChar = ""
;the following safetyMechanism is required to prevent crashes caused by fast typing. Without it, Users can type
;in between separate iterations of the menu pop up, navigating away from the page and out of the MCM itself, at
;which point trying to call ShowMessage crashes the game. This safetyMechanism prevents that from ever
;happening by terminating text input and closing all messages once focus on this option is lost.
string safetyPlace = "_root.ConfigPanelFader.configPanel.contentHolder.optionsPanel.optionsList.selectedIndex"
int safetyMechanism = UI.GetInt("Journal Menu", safetyPlace)
GoToState("RegisterInputState") ;start tracking keypresses
typingMessage = true
while typingMessage && (safetyMechanism == UI.GetInt("Journal Menu", safetyPlace))
typingMessage = false
if currentChar == ""
ShowMessage("Begin Typing a New Preset Name.\n\n(Please avoid typing very fast)", false, "Cancel")
elseif currentChar == "DEL"
if letterCounter > 0
displayString = stringUtil.subString(displayString, 0, letterCounter)
letterCounter -= 1
if !letterCounter
currentChar = ""
endif
endif
ShowMessage(displayString, true, "Confirm New Name", "Cancel")
elseif currentChar != "ENT" ;enter will close the menu
int curLength = stringUtil.getLength(displayString)
if curLength < 41 ;max character limit (this isn't required, and could be removed)
letterCounter += 1
string sTemp = stringUtil.subString(displayString, 0, letterCounter)
displayString = sTemp + currentChar
ShowMessage(displayString, true, "Confirm New Name", "Cancel")
else
ShowMessage(displayString + "\n\nCharacter Limit Reached", true, "Confirm New Name", "Cancel")
endif
endif
endWhile
GoToState("")
if (safetyMechanism == UI.GetInt("Journal Menu", safetyPlace)) && currentChar == "ENT"
if letterCounter
displayString = stringUtil.subString(displayString, 1, letterCounter)
;;
;; displayString now contains the text your user entered!
;;
endif
endif
;...
Fragment 3 -- The Input State[edit | edit source]
;////////////// INPUT STATE /////////////////;
int[] DXScanCodes
string[] DXScanChars
string displayString ;used by menu setter
bool typingMessage
bool bTypeSpeedLimiter
string currentChar ;new tester
;bool bInputPage ;used by menu setter
State RegisterInputState
Event OnBeginState()
int i = 51
while i
i -= 1
RegisterForKey(DXScanCodes[i])
endWhile
RegisterForKey(14) ;backspace
RegisterForKey(211) ;delete
RegisterForKey(28) ;enter
EndEvent
Event OnKeyDown(int code)
if !bTypeSpeedLimiter
bTypeSpeedLimiter = true
if code == 14 || code == 211 ;backspace/delete
currentChar = "DEL"
elseif code == 28 ;enter
currentChar = "ENT"
else
int index = DXScanCodes.find(code)
if index >= 0
currentChar = DXScanChars[index]
endif
endif
typingMessage = true
UnregisterForKey(28) ;only listen for user to hit enter
Input.TapKey(28)
RegisterForKey(28)
utility.waitmenumode(0.3)
;typingMessage = false
bTypeSpeedLimiter = false
endif
EndEvent
Event OnEndState()
UnregisterForAllKeys()
EndEvent
EndState