A small mod for Cyberpunk 2077 that allows other mods to easily add settings options to a custom, fully native UI Mods
settings menu. User-friendly and fully controller compatible.
- CET Version 1.18.1+ is required
- Add this mod to your mod's requirement list: Nexus page
- Import it into your mod:
nativeSettings = GetMod("nativeSettings")
- Multiple mods can share the same tab
path
should be a/
followed by a simple keywordlabel
is what will be displayedcallbackFunction
is an optional function parameter that gets called when the tab gets closednativeSettings.addTab("/myMod", "My mod", callbackFunction()) -- Add a tab (path, label, callback)
- Use subcategories to organize options
path
should be your mods tab path (e.g./myMod
), followed by a/
, followed by a simple keyword.label
is what will be displayedoptionalIndex
is an optionalint
parameter to control the position of the subcategory (Default is same order asaddSubcategory
's get called)nativeSettings.addSubcategory("/myMod/sub", "A subcategory") -- Add a subcategory (path, label, optionalIndex)
- All option widgets can be added to either a tab directly (Provide the tab path), or a tab's subcategory (Provide full path)
path
: Where the widget goes, e.g./myMod
or/myMod/sub
label
: What gets displayed to the left of the widgetdesc
: A description of what the option does, gets displayed when hovered overcurrentValue
: This is what the option's initial value (Type depends on the widget) is. Usually, this value would get read from a settings file / database insideonInit
defaultValue
: This is what the option's default value should be, gets set when theDefaults
button is hitcallback
: Here, you pass a functionf(value)
that gets called when the options gets changed. It gets called with a single parameter, the updated valueoptionalIndex
: Optional index parameter that can be used to control the order of the options (Default is same order as theaddOption
's get called)
- This adds a basic true/false switch
- Datatype is
boolean
-- Parameters: path, label, desc, currentValue, defaultValue, callback, optionalIndex nativeSettings.addSwitch("/myMod/sub", "Switch", "Description", true, true, function(state) print("Changed SWITCH to ", state) -- Add any logic you need in here, such as saving the changes to file / database end)
- This adds a slider, that can only get set to whole numbers
- Datatype is
int
min
: This is the minimum value of the slidermax
: This is the maximum value of the sliderstep
: This is the minimum amount the slider can move-- Parameters: path, label, desc, min, max, step, currentValue, defaultValue, callback, optionalIndex nativeSettings.addRangeInt("/myMod/sub", "Slider Int", "Description", 1, 100, 1, 50, 25, function(value) print("Changed SLIDER INT to ", value) -- Add any logic you need in here, such as saving the changes to file / database end)
- This adds a slider, that can be set to any value
- Datatype is
float
(int
also works) min
: This is the minimum value of the slidermax
: This is the maximum value of the sliderstep
: This is the minimum amount the slider can moveformat
: This is a format string, to control how the value gets displayed (Works the same as lua'sstring.format()
)-- Parameters: path, label, desc, min, max, step, format, currentValue, defaultValue, callback, optionalIndex nativeSettings.addRangeFloat("/myMod/sub", "Slider Float", "Description", 1, 100, 0.25, "%.2f", 50, 1, function(value) print("Changed SLIDER FLOAT to ", value) -- Add any logic you need in here, such as saving the changes to file / database end)
- This adds a list of strings, that can be chosen of
- Datatype is
table
- The table must be numerical indexed
currentValue
/defaultValue
is the index of the selected element-- Parameters: path, label, desc, elements, currentValue, defaultValue, callback, optionalIndex local list = {[1] = "Option 1", [2] = "Option 2", [3] = "Option 3", [4] = "Option 4"} -- Create list of options, with numeric index nativeSettings.addSelectorString("/myMod/sub", "String List", "Description", list, 1, 3, function(value) print("Changed LIST STRING to ", list[value]) -- Add any logic you need in here, such as saving the changes to file / database end)
- This adds a keybind widget, that can be clicked on to store any pressed key
- Datatype is
string
- When pressed, it will return the keycode e.g
IK_X
of the pressed key currentKey
anddefaultKey
needs to be a valid keycodeisHold
determines whether or not the key icon has a "Hold" outline- The actual reading of raw inputs has to be done via Codeware, for examples on how to implement this using CET, including a simple module for setting up multikey bindings, check the provided example in this repo
- Controller bindings are supported too, but require Codeware to work (Required for actually reading inputs anyways), for an example on how to forward the input events from Codeware to Native Settings check the provided examples
-- Parameters: path, label, desc, currentKey, defaultKey, isHold, callback, optionalIndex nativeSettings.addKeyBinding("/myMod/sub", "Keybind", "Description", "IK_1", "IK_5", false, function(key) print("Changed KEYBIND to", key) -- Add any logic you need in here, such as saving the changes to file / database end)
- This adds a simple, interactable button which calls the
callback
function without any parameters when clicked - Has no
currentValue
anddefaultValue
parameters buttonText
is the text that gets displayed inside the buttontextSize
is the size of thebuttonText
text-- Parameters: path, label, desc, buttonText, textSize, callback, optionalIndex nativeSettings.addButton("/myMod/sub", "Button", "Description", "Button label", 45, function() print("User clicked BUTTON") -- Add any logic you need in here, such as calling a function from your mod end)
- This is not a typical widget, i.e. it does not have any visible UI
- It can be used to get a reference to the settings screen's main
inkCompoundWidget
- With this reference you can add your own custom widgets to the settings page, such as the Furigana mod is doing
inkCompoundWidget
is the SettingsMainGameController's settingsOptionsList widget
-- Parameters: path, callback, optionalIndex
nativeSettings.addCustom("/myMod/sub", function(inkCompoundWidget)
-- Add any logic you need in here, such as adding custom UI to the inkCompoundWidget
end)
- Option widgets as well as subcategories can be added or removed while the UI is active
- Use this in combination with the
optionalIndex
parameter of anyaddOption
function to add and remove options where they are needed
optionTable
is what gets returned by anyaddOption
function (switch/int/float/list/button)-- Parameters: optionTable nativeSettings.removeOption(optionTable)
path
is the full path to the subcategory you want to remove-- Parameters: path nativeSettings.removeSubcategory("/myMod/sub")
- A custom callback function can be registered for a tab, and optionally the normal restore default actions can be overridden
-- Parameters: path, overrideNativeRestoreDefaults, callback nativeSettings.registerRestoreDefaultsCallback("/myMod", true, function() -- Handle restoring defaults with your own logic end)
- Calling this function is not necessary anymore, as of version 1.4
- Refreshes the UI when active, to reflect changes made by adding (e.g.
addSwitch
) or removing (e.g.removeOption
) option widgets or entire subcategories - When adding or removing multiple option widgets or subcategories at once, make sure to only call
refresh
once, after all adding / removing operations are donenativeSettings.refresh()
- The nativeSettings mod only gets the settings values at the startup in form of the
currentValue
- If you modify any settings / options from e.g. a secondary ImGui settings window, the values displayed by nativeSettings will be out of sync
- Use the
setOption(optionTable, value)
function if you change an option from outside the nativeSettings window, to make sure everything stays synced optionTable
is what gets returned by anyaddOption
function (switch/int/float/list)value
is the value you want to set- Example:
local settingsTables = {} -- An empty table to store the return from the addOption functions, in case we want to use setOption() or removeOption(), can be ignored otherwise local switchState = false -- Would usually get loaded from a config file / database local nativeSettings registerForEvent("onInit", function() nativeSettings = GetMod("nativeSettings") -- Get a reference to the nativeSettings mod if not nativeSettings then -- Make sure the mod is installed print("Error: NativeSettings not found!") return end nativeSettings.addTab("/myMod", "My mod") -- Add our mods tab (path, label) nativeSettings.addSubcategory("/myMod/sub", "A subcategory") -- Optional: Add a subcategory (path, label), you can add as many as you want settingsTables["switch"] = nativeSettings.addSwitch("/myMod/sub", "Switch", "Description", switchState, true, function(state) -- Setup a switch, and store its returned table print("Changed SWITCH to ", state) switchState = state end) end) registerForEvent("onDraw", function() if ImGui.Begin("Alternative Settings Window", ImGuiWindowFlags.AlwaysAutoResize) then switchState, changed = ImGui.Checkbox("Switch", switchState) if changed then -- We changed the option value from somewhere else nativeSettings.setOption(settingsTables["switch"], switchState) -- Update the value for the nativeSettings mod end end ImGui.End() end)
- psiberx for answering all my questions, as well as doing a lot of work on CET that makes this mod even work and creating
Cron.lua
,UIButton.lua
,Ref.lua
andEventProxy.lua
. - RMK for adding the keybind widget, making proper handling for adding and removing elements and generally helping with bugfixing
- dkollmann for adding the "custom" widget type and implementing the optional callback for tabs
- nim for hating ImGui